mirror of https://github.com/ElemeFE/element
Chore: Theme Extension (#16686)
parent
8b8a1a2e87
commit
f29f49a17d
|
@ -1,6 +1,7 @@
|
||||||
{
|
{
|
||||||
"globals": {
|
"globals": {
|
||||||
"ga": true
|
"ga": true,
|
||||||
|
"chrome": true
|
||||||
},
|
},
|
||||||
"plugins": ["html", "json"],
|
"plugins": ["html", "json"],
|
||||||
"extends": "elemefe",
|
"extends": "elemefe",
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
const path = require('path');
|
||||||
|
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||||
|
const demoConfig = require('./webpack.demo');
|
||||||
|
const webpack = require('webpack');
|
||||||
|
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
|
||||||
|
const VueLoaderPlugin = require('vue-loader/lib/plugin');
|
||||||
|
|
||||||
|
demoConfig.entry = {
|
||||||
|
background: path.join(process.cwd(), './examples/extension/src/background'),
|
||||||
|
entry: path.join(process.cwd(), './examples/extension/src/entry')
|
||||||
|
};
|
||||||
|
demoConfig.output = {
|
||||||
|
path: path.join(process.cwd(), './examples/extension/dist'),
|
||||||
|
filename: '[name].js'
|
||||||
|
};
|
||||||
|
demoConfig.plugins = [
|
||||||
|
new CopyWebpackPlugin([
|
||||||
|
{ from: 'examples/extension/src/manifest.json' },
|
||||||
|
{ from: 'examples/extension/src/icon.png' }
|
||||||
|
]),
|
||||||
|
new VueLoaderPlugin(),
|
||||||
|
new ProgressBarPlugin(),
|
||||||
|
new webpack.LoaderOptionsPlugin({
|
||||||
|
vue: {
|
||||||
|
compilerOptions: {
|
||||||
|
preserveWhitespace: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
new webpack.HotModuleReplacementPlugin()
|
||||||
|
];
|
||||||
|
demoConfig.module.rules.find(a => a.loader === 'url-loader').query = {};
|
||||||
|
module.exports = demoConfig;
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<section class="config" :key="displayName">
|
<section class="config" :key="displayName">
|
||||||
<div class="config-label">
|
<div class="config-label">
|
||||||
<el-tooltip :content="displayName">
|
<el-tooltip :content="displayName" placement="top">
|
||||||
<span>{{displayKeyName}}</span>
|
<span>{{displayKeyName}}</span>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<section class="config" :key="displayName">
|
<section class="config" :key="displayName">
|
||||||
<div class="config-label">
|
<div class="config-label">
|
||||||
<el-tooltip :content="displayName">
|
<el-tooltip :content="displayName" placement="top">
|
||||||
<span>{{displayKeyName}}</span>
|
<span>{{displayKeyName}}</span>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-button
|
<el-button
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<section class="config" :key="displayName">
|
<section class="config" :key="displayName">
|
||||||
<div class="config-label">
|
<div class="config-label">
|
||||||
<el-tooltip :content="displayName">
|
<el-tooltip :content="displayName" placement="top">
|
||||||
<span>{{displayKeyName}}</span>
|
<span>{{displayKeyName}}</span>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<section class="config" :key="displayName">
|
<section class="config" :key="displayName">
|
||||||
<div class="config-label">
|
<div class="config-label">
|
||||||
<el-tooltip :content="displayName">
|
<el-tooltip :content="displayName" placement="top">
|
||||||
<span>{{displayKeyName}}</span>
|
<span>{{displayKeyName}}</span>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<section class="config" :key="displayName">
|
<section class="config" :key="displayName">
|
||||||
<div class="config-label">
|
<div class="config-label">
|
||||||
<el-tooltip :content="displayName">
|
<el-tooltip :content="displayName" placement="top">
|
||||||
<span>{{displayKeyName}}</span>
|
<span>{{displayKeyName}}</span>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<section class="config" :key="displayName">
|
<section class="config" :key="displayName">
|
||||||
<div class="config-label">
|
<div class="config-label">
|
||||||
<el-tooltip :content="displayName">
|
<el-tooltip :content="displayName" placement="top">
|
||||||
<span>{{displayKeyName}}</span>
|
<span>{{displayKeyName}}</span>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<section class="config" :key="displayName">
|
<section class="config" :key="displayName">
|
||||||
<div class="config-label">
|
<div class="config-label">
|
||||||
<el-tooltip :content="displayName">
|
<el-tooltip :content="displayName" placement="top">
|
||||||
<span>{{displayKeyName}}</span>
|
<span>{{displayKeyName}}</span>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -102,7 +102,7 @@ export default {
|
||||||
defaultConfig = res;
|
defaultConfig = res;
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
this.onError(err);
|
this.onError && this.onError(err);
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="main" ref="mainPanel">
|
<div class="editor-main" ref="mainPanel">
|
||||||
<!-- <span>{{configName}}</span> -->
|
<!-- <span>{{configName}}</span> -->
|
||||||
<div v-for="(config, key) in configByOrder" :key="key">
|
<div v-for="(config, key) in configByOrder" :key="key">
|
||||||
<span
|
<span
|
||||||
|
@ -22,7 +22,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.main {
|
.editor-main {
|
||||||
padding: 0 18px 15px;
|
padding: 0 18px 15px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
|
@ -260,7 +260,8 @@ export default {
|
||||||
base: {
|
base: {
|
||||||
type: String,
|
type: String,
|
||||||
default: ''
|
default: ''
|
||||||
}
|
},
|
||||||
|
from: String
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -308,6 +309,10 @@ export default {
|
||||||
switch (e) {
|
switch (e) {
|
||||||
case 'preview':
|
case 'preview':
|
||||||
case 'edit':
|
case 'edit':
|
||||||
|
if (this.from) {
|
||||||
|
this.$emit('action', e, this.config);
|
||||||
|
return;
|
||||||
|
}
|
||||||
const { name, theme } = this.config;
|
const { name, theme } = this.config;
|
||||||
savePreviewToLocal({
|
savePreviewToLocal({
|
||||||
type: this.type,
|
type: this.type,
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
dist
|
|
@ -0,0 +1,18 @@
|
||||||
|
import Vue from 'vue';
|
||||||
|
import App from './editor/index';
|
||||||
|
import Element from 'main/index.js';
|
||||||
|
import 'packages/theme-chalk/src/index.scss';
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
Vue.use(Element, { zIndex: 100000 });
|
||||||
|
const root = document.createElement('div');
|
||||||
|
document.body.appendChild(root);
|
||||||
|
|
||||||
|
window.ga = function() {
|
||||||
|
console.log(arguments);
|
||||||
|
};
|
||||||
|
|
||||||
|
new Vue({ // eslint-disable-line
|
||||||
|
render: h => h(App)
|
||||||
|
}).$mount(root);
|
||||||
|
};
|
|
@ -0,0 +1,6 @@
|
||||||
|
chrome.browserAction.onClicked.addListener(tab => {
|
||||||
|
chrome.tabs.executeScript(tab.id, {
|
||||||
|
file: 'entry.js'
|
||||||
|
});
|
||||||
|
})
|
||||||
|
;
|
|
@ -0,0 +1,212 @@
|
||||||
|
<template>
|
||||||
|
<div class="ext-panel" :class="{moving : moving}" :style="{top: `${top}px`, left: `${left}px`}" ref="editor" >
|
||||||
|
<img class="entrance touch-icon" src="./icon-entrance.png" v-show="!showSidebar" @click="toggleSidebar" />
|
||||||
|
<img class="close touch-icon" src="./icon-close.png" v-show="showSidebar" @click="toggleSidebar" />
|
||||||
|
<div class="editor" :style="{height: `${height}px`}" v-show="showSidebar">
|
||||||
|
<el-tabs v-model="activeTab" @tab-click="onTabClick">
|
||||||
|
<el-tab-pane label="Config" name="config">
|
||||||
|
<theme-configurator
|
||||||
|
:themeConfig="themeConfig"
|
||||||
|
:previewConfig="previewConfig"
|
||||||
|
:onUserConfigUpdate="onUserConfigUpdate"
|
||||||
|
from="extension"
|
||||||
|
></theme-configurator>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="Gallery" name="gallery">
|
||||||
|
<gallery
|
||||||
|
ref='gallery'
|
||||||
|
:height="height"
|
||||||
|
:width="width - 7"
|
||||||
|
@action="onGalleryAction"
|
||||||
|
/>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ThemeConfigurator from '../../../components/theme-configurator';
|
||||||
|
import themeLoader from '../../../components/theme/loader';
|
||||||
|
import gallery from './gallery';
|
||||||
|
import { loadUserThemeFromLocal, saveUserThemeToLocal } from './utils';
|
||||||
|
import bus from '../../../bus.js';
|
||||||
|
import {
|
||||||
|
ACTION_APPLY_THEME
|
||||||
|
} from '../../../components/theme/constant.js';
|
||||||
|
|
||||||
|
let initX;
|
||||||
|
let initY;
|
||||||
|
let leftX = 0;
|
||||||
|
let topY = 25;
|
||||||
|
export default {
|
||||||
|
mixins: [themeLoader],
|
||||||
|
components: {
|
||||||
|
ThemeConfigurator,
|
||||||
|
gallery
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showSidebar: true,
|
||||||
|
previewConfig: {},
|
||||||
|
themeConfig: {},
|
||||||
|
top: topY,
|
||||||
|
left: leftX,
|
||||||
|
height: window.innerHeight - 30 * 2,
|
||||||
|
width: 0,
|
||||||
|
moving: false,
|
||||||
|
activeTab: 'config',
|
||||||
|
themeName: '',
|
||||||
|
userTheme: []
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
const editor = this.$refs.editor;
|
||||||
|
this.width = editor.offsetWidth;
|
||||||
|
leftX = window.innerWidth - 20 - this.width;
|
||||||
|
this.left = leftX;
|
||||||
|
editor.addEventListener('mousedown', e => {
|
||||||
|
initX = e.clientX;
|
||||||
|
initY = e.clientY;
|
||||||
|
leftX = this.left;
|
||||||
|
topY = this.top;
|
||||||
|
document.addEventListener('mousemove', this.moveFunc);
|
||||||
|
});
|
||||||
|
document.addEventListener('mouseup', e => {
|
||||||
|
document.removeEventListener('mousemove', this.moveFunc);
|
||||||
|
setTimeout(() => {this.moving = false;}, 0);
|
||||||
|
});
|
||||||
|
// chrome.storage.local.remove('ELEMENT_THEME_USER_CONFIG');
|
||||||
|
loadUserThemeFromLocal()
|
||||||
|
.then((result) => {
|
||||||
|
if (result) {
|
||||||
|
this.activeTab = 'gallery';
|
||||||
|
this.userTheme = result;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
checkLocalThemeConfig() {}, // disable mixin auto loading
|
||||||
|
toggleSidebar() {
|
||||||
|
if (this.moving) return;
|
||||||
|
this.showSidebar = !this.showSidebar;
|
||||||
|
if (!this.showSidebar) {
|
||||||
|
const windowWidth = window.innerWidth;
|
||||||
|
if (this.left + this.width * 0.5 < windowWidth * 0.5) {
|
||||||
|
this.left = 0;
|
||||||
|
} else {
|
||||||
|
this.left = windowWidth - 50;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.moveEditor(this.left, this.top);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
moveFunc(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const deltaX = initX - e.clientX;
|
||||||
|
const deltaY = initY - e.clientY;
|
||||||
|
if (Math.abs(deltaX) > 5 || Math.abs(deltaY) > 5) {
|
||||||
|
this.moving = true;
|
||||||
|
}
|
||||||
|
this.moveEditor(leftX - deltaX, topY - deltaY);
|
||||||
|
},
|
||||||
|
moveEditor(x, y) {
|
||||||
|
const showSidebar = this.showSidebar;
|
||||||
|
let nextTop = y;
|
||||||
|
if (nextTop < 0) nextTop = 0;
|
||||||
|
const maxTop = window.innerHeight - (showSidebar ? (this.height + 5) : 50);
|
||||||
|
if (nextTop > maxTop) nextTop = maxTop;
|
||||||
|
this.top = nextTop;
|
||||||
|
let nextLeft = x;
|
||||||
|
if (nextLeft < 0) nextLeft = 0;
|
||||||
|
const maxLeft = window.innerWidth - (showSidebar ? (this.width + 5) : 50);
|
||||||
|
if (nextLeft > maxLeft) nextLeft = maxLeft;
|
||||||
|
this.left = nextLeft;
|
||||||
|
},
|
||||||
|
onGalleryAction(key, value) {
|
||||||
|
switch (key) {
|
||||||
|
case 'edit':
|
||||||
|
this.themeName = value.name;
|
||||||
|
this.themeConfig = JSON.parse(value.theme);
|
||||||
|
bus.$emit(ACTION_APPLY_THEME, this.themeConfig);
|
||||||
|
this.activeTab = 'config';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onTabClick(e) {
|
||||||
|
if (e && e.name === 'gallery') {
|
||||||
|
this.$refs.gallery.init();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onUserConfigUpdate(userConfig) {
|
||||||
|
if (this.themeName) {
|
||||||
|
this.userTheme.forEach((config) => {
|
||||||
|
if (config.name === this.themeName) {
|
||||||
|
config.update = Date.now();
|
||||||
|
config.theme = JSON.stringify(userConfig);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.themeName = `Theme-${Date.now()}`;
|
||||||
|
this.userTheme.push({
|
||||||
|
update: Date.now(),
|
||||||
|
name: this.themeName,
|
||||||
|
theme: JSON.stringify(userConfig)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
saveUserThemeToLocal(this.userTheme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.ext-panel {
|
||||||
|
position: fixed;
|
||||||
|
background: transparent;
|
||||||
|
user-select: none;
|
||||||
|
z-index: 100000;
|
||||||
|
}
|
||||||
|
.ext-panel.moving{
|
||||||
|
cursor: move;
|
||||||
|
}
|
||||||
|
.ext-panel.moving .touch-icon{
|
||||||
|
cursor: move;
|
||||||
|
}
|
||||||
|
.ext-panel .touch-icon{
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.ext-panel .close {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
height: 20px;
|
||||||
|
width: 20px;
|
||||||
|
z-index: 100001;
|
||||||
|
}
|
||||||
|
.ext-panel .entrance {
|
||||||
|
height: 50px;
|
||||||
|
width: 50px;
|
||||||
|
}
|
||||||
|
.ext-panel .editor {
|
||||||
|
overflow: hidden;
|
||||||
|
background: #f5f7fa;
|
||||||
|
border: 1px solid #ebeef5;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
|
||||||
|
margin: 5px 5px 0 0;
|
||||||
|
min-width: 261px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<style>
|
||||||
|
.ext-panel .editor .el-tabs__content, .ext-panel .editor .el-tabs--top, .ext-panel .editor .el-tab-pane {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.ext-panel .el-tabs__nav-scroll >div {
|
||||||
|
transform: translateX(60px)!important;
|
||||||
|
}
|
||||||
|
.ext-panel .editor-main {
|
||||||
|
padding: 0 18px 70px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,185 @@
|
||||||
|
<template>
|
||||||
|
<div :style="{
|
||||||
|
height: `${height}px`,
|
||||||
|
width: `${width}px`
|
||||||
|
}"
|
||||||
|
class="main"
|
||||||
|
>
|
||||||
|
<ul
|
||||||
|
class="theme-card-list"
|
||||||
|
>
|
||||||
|
<li class="theme-card" v-for="item in userTheme" :key="item.name">
|
||||||
|
<theme-card type="user" :config="item" @action="onAction" from="extension"></theme-card>
|
||||||
|
</li>
|
||||||
|
<li class="theme-card">
|
||||||
|
<theme-card type="upload" :config="{name: 'upload'}" @action="onAction"></theme-card>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<el-dialog :visible.sync="copyDialogVisible" :modal-append-to-body="false">
|
||||||
|
<el-form :model="copyForm" ref="copyForm" :rules="copyFormRule">
|
||||||
|
<el-form-item label="主题名称" prop="name">
|
||||||
|
<el-input v-model="copyForm.name"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="closeCopyForm">取消</el-button>
|
||||||
|
<el-button type="primary" @click="copyToUser">确认</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
ul, li {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.main {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.theme-card-list {
|
||||||
|
padding-bottom: 50px;
|
||||||
|
width: 90%;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
.theme-card {
|
||||||
|
display: inline-block;
|
||||||
|
height: 140px;
|
||||||
|
height: 15vw;
|
||||||
|
width: 100%;
|
||||||
|
max-height: 230px;
|
||||||
|
flex: 0 0 24%;
|
||||||
|
cursor: default;
|
||||||
|
vertical-align: bottom;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<style>
|
||||||
|
.theme-card .theme-card-item {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
.theme-card .theme-card-item.is-upload {
|
||||||
|
height: 80%
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ThemeCard from '../../../components/theme/theme-card.vue';
|
||||||
|
import { loadUserThemeFromLocal, saveUserThemeToLocal } from './utils';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
height: Number,
|
||||||
|
width: Number
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
userTheme: [],
|
||||||
|
copyDialogVisible: false,
|
||||||
|
copyForm: {},
|
||||||
|
copyFormRule: {
|
||||||
|
name: [{
|
||||||
|
validator: this.validateCopyName,
|
||||||
|
trigger: 'blur'
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
ThemeCard
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.init();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
init() {
|
||||||
|
loadUserThemeFromLocal().then(result => {
|
||||||
|
if (!result) return;
|
||||||
|
this.userTheme = result;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onAction(key, value) {
|
||||||
|
switch (key) {
|
||||||
|
case 'copy':
|
||||||
|
this.openCopyForm(value.theme);
|
||||||
|
break;
|
||||||
|
case 'upload':
|
||||||
|
this.openCopyForm(value);
|
||||||
|
break;
|
||||||
|
case 'delete':
|
||||||
|
this.$confirm('确定要删除这个主题?', '提示', {
|
||||||
|
confirmButtonText: '确认',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
this.deleteUserThemeByName(value.name);
|
||||||
|
}).catch(() => {});
|
||||||
|
break;
|
||||||
|
case 'rename':
|
||||||
|
this.openRenameForm(value.name);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this.$emit('action', key, value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
openCopyForm(theme) {
|
||||||
|
this.copyForm.theme = theme;
|
||||||
|
this.copyDialogVisible = true;
|
||||||
|
},
|
||||||
|
openRenameForm(name) {
|
||||||
|
this.copyForm.oldname = name;
|
||||||
|
this.copyDialogVisible = true;
|
||||||
|
},
|
||||||
|
closeCopyForm() {
|
||||||
|
this.copyDialogVisible = false;
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.copyForm = {};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
validateCopyName(rule, value, callback) {
|
||||||
|
if (!value) {
|
||||||
|
callback(new Error('主题名称是必填项'));
|
||||||
|
} else if (this.filterUserThemeByName(value).length > 0) {
|
||||||
|
callback(new Error('主题名称重复'));
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
copyToUser() {
|
||||||
|
this.$refs.copyForm.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
const { theme, name, oldname } = this.copyForm;
|
||||||
|
if (theme) {
|
||||||
|
// copy
|
||||||
|
this.userTheme.push({
|
||||||
|
update: Date.now(),
|
||||||
|
name,
|
||||||
|
theme
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// rename
|
||||||
|
this.userTheme.forEach((config) => {
|
||||||
|
if (config.name === oldname) {
|
||||||
|
config.update = Date.now();
|
||||||
|
config.name = name;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.saveToLocal();
|
||||||
|
this.closeCopyForm();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
filterUserThemeByName(name, include = true) {
|
||||||
|
return this.userTheme.filter((theme) => (include ? theme.name === name : theme.name !== name));
|
||||||
|
},
|
||||||
|
saveToLocal() {
|
||||||
|
saveUserThemeToLocal(this.userTheme);
|
||||||
|
},
|
||||||
|
deleteUserThemeByName(name) {
|
||||||
|
this.userTheme = this.filterUserThemeByName(name, false);
|
||||||
|
this.saveToLocal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 6.2 KiB |
|
@ -0,0 +1,13 @@
|
||||||
|
<template>
|
||||||
|
<editor />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import editor from './editor';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
editor
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,18 @@
|
||||||
|
const ELEMENT_THEME_USER_CONFIG = 'ELEMENT_THEME_USER_CONFIG';
|
||||||
|
export const loadFromLocal = (key) => {
|
||||||
|
return new window.Promise((resolve) => {
|
||||||
|
chrome.storage.local.get([key], (result) => {
|
||||||
|
resolve(result[key]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const saveToLocal = (key, value) => {
|
||||||
|
chrome.storage.local.set({[key]: value});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const loadUserThemeFromLocal = () => {
|
||||||
|
return loadFromLocal(ELEMENT_THEME_USER_CONFIG);
|
||||||
|
};
|
||||||
|
export const saveUserThemeToLocal = (value) => {
|
||||||
|
saveToLocal(ELEMENT_THEME_USER_CONFIG, value);
|
||||||
|
};
|
|
@ -0,0 +1,7 @@
|
||||||
|
import init from './app';
|
||||||
|
|
||||||
|
if (!window.ElementThemeRollerInit) {
|
||||||
|
window.ElementThemeRollerInit = true;
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"name": "Element Theme Roller",
|
||||||
|
"version": "0.0.9",
|
||||||
|
"description": "Customize all Design Tokens of Element UI and preview in real-time",
|
||||||
|
"web_accessible_resources": ["entry.js"],
|
||||||
|
"background": {
|
||||||
|
"scripts": [
|
||||||
|
"background.js"
|
||||||
|
],
|
||||||
|
"persistent": true
|
||||||
|
},
|
||||||
|
"icons": {
|
||||||
|
"128": "icon.png"
|
||||||
|
},
|
||||||
|
"browser_action": {
|
||||||
|
"default_icon": "icon.png",
|
||||||
|
"default_title": "Element Theme Roller"
|
||||||
|
},
|
||||||
|
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
|
||||||
|
"manifest_version": 2,
|
||||||
|
"permissions": [
|
||||||
|
"activeTab",
|
||||||
|
"storage",
|
||||||
|
"https://*.ele.me/",
|
||||||
|
"https://*.elenet.me/"
|
||||||
|
]
|
||||||
|
}
|
|
@ -18,6 +18,8 @@
|
||||||
"build:umd": "node build/bin/build-locale.js",
|
"build:umd": "node build/bin/build-locale.js",
|
||||||
"clean": "rimraf lib && rimraf packages/*/lib && rimraf test/**/coverage",
|
"clean": "rimraf lib && rimraf packages/*/lib && rimraf test/**/coverage",
|
||||||
"deploy:build": "npm run build:file && cross-env NODE_ENV=production webpack --config build/webpack.demo.js && echo element.eleme.io>>examples/element-ui/CNAME",
|
"deploy:build": "npm run build:file && cross-env NODE_ENV=production webpack --config build/webpack.demo.js && echo element.eleme.io>>examples/element-ui/CNAME",
|
||||||
|
"deploy:extension": "cross-env NODE_ENV=production webpack --config build/webpack.extension.js",
|
||||||
|
"dev:extension": "rimraf examples/extension/dist && cross-env NODE_ENV=development webpack --watch --config build/webpack.extension.js",
|
||||||
"dev": "npm run bootstrap && npm run build:file && cross-env NODE_ENV=development webpack-dev-server --config build/webpack.demo.js & node build/bin/template.js",
|
"dev": "npm run bootstrap && npm run build:file && cross-env NODE_ENV=development webpack-dev-server --config build/webpack.demo.js & node build/bin/template.js",
|
||||||
"dev:play": "npm run build:file && cross-env NODE_ENV=development PLAY_ENV=true webpack-dev-server --config build/webpack.demo.js",
|
"dev:play": "npm run build:file && cross-env NODE_ENV=development PLAY_ENV=true webpack-dev-server --config build/webpack.demo.js",
|
||||||
"dist": "npm run clean && npm run build:file && npm run lint && webpack --config build/webpack.conf.js && webpack --config build/webpack.common.js && webpack --config build/webpack.component.js && npm run build:utils && npm run build:umd && npm run build:theme",
|
"dist": "npm run clean && npm run build:file && npm run lint && webpack --config build/webpack.conf.js && webpack --config build/webpack.common.js && webpack --config build/webpack.component.js && npm run build:utils && npm run build:umd && npm run build:theme",
|
||||||
|
|
Loading…
Reference in New Issue