style: 全局代码格式化

Former-commit-id: bb50c8419b8fcdeb48c93fce9f399d901e8f5a52
This commit is contained in:
郝先瑞
2022-05-04 15:02:33 +08:00
parent e563bc340c
commit 11f02c0254
136 changed files with 11147 additions and 9780 deletions

View File

@@ -1,101 +1,107 @@
<template>
<el-breadcrumb
class="app-breadcrumb"
separator-class="el-icon-arrow-right"
>
<transition-group name="breadcrumb">
<el-breadcrumb-item
v-for="(item, index) in breadcrumbs"
:key="item.path"
>
<span
v-if="item.redirect === 'noredirect' || index === breadcrumbs.length-1"
class="no-redirect"
>{{ generateTitle(item.meta.title) }}</span>
<a v-else @click.prevent="handleLink(item)">
{{ generateTitle(item.meta.title) }}
</a>
</el-breadcrumb-item>
</transition-group>
</el-breadcrumb>
<el-breadcrumb class="app-breadcrumb" separator-class="el-icon-arrow-right">
<transition-group name="breadcrumb">
<el-breadcrumb-item v-for="(item, index) in breadcrumbs" :key="item.path">
<span
v-if="
item.redirect === 'noredirect' || index === breadcrumbs.length - 1
"
class="no-redirect"
>{{ generateTitle(item.meta.title) }}</span
>
<a v-else @click.prevent="handleLink(item)">
{{ generateTitle(item.meta.title) }}
</a>
</el-breadcrumb-item>
</transition-group>
</el-breadcrumb>
</template>
<script setup lang="ts">
import {onBeforeMount, ref, watch} from 'vue'
import {useRoute, RouteLocationMatched} from 'vue-router'
import {compile} from 'path-to-regexp'
import router from '@/router'
import {generateTitle} from '@/utils/i18n'
import { onBeforeMount, ref, watch } from 'vue';
import { useRoute, RouteLocationMatched } from 'vue-router';
import { compile } from 'path-to-regexp';
import router from '@/router';
import { generateTitle } from '@/utils/i18n';
const currentRoute = useRoute()
const currentRoute = useRoute();
const pathCompile = (path: string) => {
const {params} = currentRoute
const toPath = compile(path)
return toPath(params)
}
const { params } = currentRoute;
const toPath = compile(path);
return toPath(params);
};
const breadcrumbs = ref([] as Array<RouteLocationMatched>)
const breadcrumbs = ref([] as Array<RouteLocationMatched>);
function getBreadcrumb() {
let matched = currentRoute.matched.filter((item) => item.meta && item.meta.title)
const first = matched[0]
if (!isDashboard(first)) {
matched = [{path: '/dashboard', meta: {title: 'dashboard'}} as any].concat(matched)
}
breadcrumbs.value = matched.filter((item) => {
return item.meta && item.meta.title && item.meta.breadcrumb !== false
})
let matched = currentRoute.matched.filter(
item => item.meta && item.meta.title
);
const first = matched[0];
if (!isDashboard(first)) {
matched = [
{ path: '/dashboard', meta: { title: 'dashboard' } } as any
].concat(matched);
}
breadcrumbs.value = matched.filter(item => {
return item.meta && item.meta.title && item.meta.breadcrumb !== false;
});
}
function isDashboard(route: RouteLocationMatched) {
const name = route && route.name
if (!name) {
return false
}
return name.toString().trim().toLocaleLowerCase() === 'Dashboard'.toLocaleLowerCase()
const name = route && route.name;
if (!name) {
return false;
}
return (
name.toString().trim().toLocaleLowerCase() ===
'Dashboard'.toLocaleLowerCase()
);
}
function handleLink(item: any) {
const {redirect, path} = item
if (redirect) {
router.push(redirect).catch((err) => {
console.warn(err)
})
return
}
router.push(pathCompile(path)).catch((err) => {
console.warn(err)
})
const { redirect, path } = item;
if (redirect) {
router.push(redirect).catch(err => {
console.warn(err);
});
return;
}
router.push(pathCompile(path)).catch(err => {
console.warn(err);
});
}
watch(() => currentRoute.path, (path) => {
if (path.startsWith('/redirect/')) {
return
}
getBreadcrumb()
})
watch(
() => currentRoute.path,
path => {
if (path.startsWith('/redirect/')) {
return;
}
getBreadcrumb();
}
);
onBeforeMount(() => {
getBreadcrumb()
})
getBreadcrumb();
});
</script>
<style lang="scss" scoped>
.el-breadcrumb__inner,
.el-breadcrumb__inner a {
font-weight: 400 !important;
font-weight: 400 !important;
}
.app-breadcrumb.el-breadcrumb {
display: inline-block;
font-size: 14px;
line-height: 50px;
margin-left: 8px;
display: inline-block;
font-size: 14px;
line-height: 50px;
margin-left: 8px;
.no-redirect {
color: #97a8be;
cursor: text;
}
.no-redirect {
color: #97a8be;
cursor: text;
}
}
</style>
</style>

View File

@@ -1,54 +1,59 @@
<template>
<a href="https://github.com/hxrui" target="_blank" class="github-corner" aria-label="View source on Github">
<svg
width="80"
height="80"
viewBox="0 0 250 250"
style="fill:#40c9c6; color:#fff;"
aria-hidden="true"
>
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z" />
<path
d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
fill="currentColor"
style="transform-origin: 130px 106px;"
class="octo-arm"
/>
<path
d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
fill="currentColor"
class="octo-body"
/>
</svg>
</a>
<a
href="https://github.com/hxrui"
target="_blank"
class="github-corner"
aria-label="View source on Github"
>
<svg
width="80"
height="80"
viewBox="0 0 250 250"
style="fill: #40c9c6; color: #fff"
aria-hidden="true"
>
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z" />
<path
d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
fill="currentColor"
style="transform-origin: 130px 106px"
class="octo-arm"
/>
<path
d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
fill="currentColor"
class="octo-body"
/>
</svg>
</a>
</template>
<style scoped>
.github-corner:hover .octo-arm {
animation: octocat-wave 560ms ease-in-out
animation: octocat-wave 560ms ease-in-out;
}
@keyframes octocat-wave {
0%,
100% {
transform: rotate(0)
}
20%,
60% {
transform: rotate(-25deg)
}
40%,
80% {
transform: rotate(10deg)
}
0%,
100% {
transform: rotate(0);
}
20%,
60% {
transform: rotate(-25deg);
}
40%,
80% {
transform: rotate(10deg);
}
}
@media (max-width:500px) {
.github-corner:hover .octo-arm {
animation: none
}
.github-corner .octo-arm {
animation: octocat-wave 560ms ease-in-out
}
@media (max-width: 500px) {
.github-corner:hover .octo-arm {
animation: none;
}
.github-corner .octo-arm {
animation: octocat-wave 560ms ease-in-out;
}
}
</style>

View File

@@ -1,44 +1,46 @@
<template>
<div style="padding: 0 15px;" @click="toggleClick">
<svg
:class="{'is-active':isActive}"
class="hamburger"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
width="64"
height="64"
>
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z" />
</svg>
</div>
<div style="padding: 0 15px" @click="toggleClick">
<svg
:class="{ 'is-active': isActive }"
class="hamburger"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
width="64"
height="64"
>
<path
d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z"
/>
</svg>
</div>
</template>
<script>
export default {
name: 'Hamburger',
props: {
isActive: {
type: Boolean,
default: false
}
},
methods: {
toggleClick() {
this.$emit('toggleClick')
}
}
}
name: 'Hamburger',
props: {
isActive: {
type: Boolean,
default: false
}
},
methods: {
toggleClick() {
this.$emit('toggleClick');
}
}
};
</script>
<style scoped>
.hamburger {
display: inline-block;
vertical-align: middle;
width: 20px;
height: 20px;
display: inline-block;
vertical-align: middle;
width: 20px;
height: 20px;
}
.hamburger.is-active {
transform: rotate(180deg);
transform: rotate(180deg);
}
</style>

View File

@@ -1,27 +1,40 @@
<template>
<div class="icon-select">
<el-input v-model="iconName" clearable placeholder="请输入图标名称" @clear="filterIcons"
@input="filterIcons">
<template #suffix><i class="el-icon-search el-input__icon" /></template>
</el-input>
<div class="icon-select__list">
<div v-for="(item, index) in iconList" :key="index" @click="selectedIcon(item)">
<svg-icon color="#999" :icon-class="item" style="height: 30px;width: 16px;margin-right: 5px" />
<span>{{ item }}</span>
</div>
</div>
</div>
<div class="icon-select">
<el-input
v-model="iconName"
clearable
placeholder="请输入图标名称"
@clear="filterIcons"
@input="filterIcons"
>
<template #suffix><i class="el-icon-search el-input__icon" /></template>
</el-input>
<div class="icon-select__list">
<div
v-for="(item, index) in iconList"
:key="index"
@click="selectedIcon(item)"
>
<svg-icon
color="#999"
:icon-class="item"
style="height: 30px; width: 16px; margin-right: 5px"
/>
<span>{{ item }}</span>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { ref } from 'vue';
import SvgIcon from '@/components/SvgIcon/index.vue';
const icons = [] as string[]
const icons = [] as string[];
const modules = import.meta.glob('../../assets/icons/*.svg');
for (const path in modules) {
const p = path.split('assets/icons/')[1].split('.svg')[0];
icons.push(p);
const p = path.split('assets/icons/')[1].split('.svg')[0];
icons.push(p);
}
const iconList = ref(icons);
@@ -30,51 +43,51 @@ const iconName = ref('');
const emit = defineEmits(['selected']);
function filterIcons() {
iconList.value = icons
if (iconName.value) {
iconList.value = icons.filter(item => item.indexOf(iconName.value) !== -1)
}
iconList.value = icons;
if (iconName.value) {
iconList.value = icons.filter(item => item.indexOf(iconName.value) !== -1);
}
}
function selectedIcon(name: string) {
emit('selected', name)
document.body.click()
emit('selected', name);
document.body.click();
}
function reset() {
iconName.value = ''
iconList.value = icons
iconName.value = '';
iconList.value = icons;
}
defineExpose({
reset
})
reset
});
</script>
<style lang='scss' scoped>
<style lang="scss" scoped>
.icon-select {
width: 100%;
padding: 10px;
width: 100%;
padding: 10px;
&__list {
height: 200px;
overflow-y: scroll;
&__list {
height: 200px;
overflow-y: scroll;
div {
height: 30px;
line-height: 30px;
margin-bottom: -5px;
cursor: pointer;
width: 33%;
float: left;
}
div {
height: 30px;
line-height: 30px;
margin-bottom: -5px;
cursor: pointer;
width: 33%;
float: left;
}
span {
display: inline-block;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
}
span {
display: inline-block;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
}
}
</style>
</style>

View File

@@ -1,49 +1,47 @@
<template>
<el-dropdown class="lang-select" trigger="click" @command="handleSetLanguage">
<div class="lang-select__icon">
<svg-icon class-name="international-icon" icon-class="language"/>
</div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item :disabled="language==='zh-cn'" command="zh-cn">
中文
</el-dropdown-item>
<el-dropdown-item :disabled="language==='en'" command="en">
English
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-dropdown class="lang-select" trigger="click" @command="handleSetLanguage">
<div class="lang-select__icon">
<svg-icon class-name="international-icon" icon-class="language" />
</div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item :disabled="language === 'zh-cn'" command="zh-cn">
中文
</el-dropdown-item>
<el-dropdown-item :disabled="language === 'en'" command="en">
English
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
<script setup lang="ts">
import { computed } from 'vue';
import useStore from '@/store';
import {computed} from "vue";
import useStore from "@/store";
const { app } = useStore();
const language = computed(() => app.language);
const {app}=useStore()
const language = computed(() => app.language)
import { useI18n } from 'vue-i18n';
import { ElMessage } from 'element-plus';
import SvgIcon from '@/components/SvgIcon/index.vue';
import {useI18n} from 'vue-i18n'
import {ElMessage} from 'element-plus'
import SvgIcon from '@/components/SvgIcon/index.vue'
const {locale} = useI18n()
const { locale } = useI18n();
function handleSetLanguage(lang: string) {
locale.value = lang
app.setLanguage(lang)
if (lang == 'en') {
ElMessage.success('Switch Language Successful!')
} else {
ElMessage.success('切换语言成功!')
}
locale.value = lang;
app.setLanguage(lang);
if (lang == 'en') {
ElMessage.success('Switch Language Successful!');
} else {
ElMessage.success('切换语言成功!');
}
}
</script>
<style lang='scss' scoped>
<style lang="scss" scoped>
.lang-select__icon {
line-height: 50px;
line-height: 50px;
}
</style>
</style>

View File

@@ -1,94 +1,101 @@
<template>
<div :class="{ 'hidden': hidden }" class="pagination-container">
<el-pagination :background="background" v-model:current-page="currentPage" v-model:page-size="pageSize"
:layout="layout" :page-sizes="pageSizes" :total="total" @size-change="handleSizeChange"
@current-change="handleCurrentChange" />
</div>
<div :class="{ hidden: hidden }" class="pagination-container">
<el-pagination
:background="background"
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:layout="layout"
:page-sizes="pageSizes"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</template>
<script setup lang="ts">
import { computed, PropType } from "vue";
import { scrollTo } from '@/utils/scroll-to'
import { computed, PropType } from 'vue';
import { scrollTo } from '@/utils/scroll-to';
const props = defineProps({
total: {
required: true,
type: Number as PropType<number>,
default: 0
},
page: {
type: Number,
default: 1
},
limit: {
type: Number,
default: 20
},
pageSizes: {
type: Array as PropType<number[]>,
default() {
return [10, 20, 30, 50]
}
},
layout: {
type: String,
default: 'total, sizes, prev, pager, next, jumper'
},
background: {
type: Boolean,
default: true
},
autoScroll: {
type: Boolean,
default: true
},
hidden: {
type: Boolean,
default: false
}
})
total: {
required: true,
type: Number as PropType<number>,
default: 0
},
page: {
type: Number,
default: 1
},
limit: {
type: Number,
default: 20
},
pageSizes: {
type: Array as PropType<number[]>,
default() {
return [10, 20, 30, 50];
}
},
layout: {
type: String,
default: 'total, sizes, prev, pager, next, jumper'
},
background: {
type: Boolean,
default: true
},
autoScroll: {
type: Boolean,
default: true
},
hidden: {
type: Boolean,
default: false
}
});
const emit = defineEmits(["update:page", "update:limit", "pagination"]);
const emit = defineEmits(['update:page', 'update:limit', 'pagination']);
const currentPage = computed<number | undefined>({
get: () => props.page,
set: (value) => {
emit('update:page', value)
}
})
get: () => props.page,
set: value => {
emit('update:page', value);
}
});
const pageSize = computed<number | undefined>({
get() {
return props.limit
},
set(val) {
emit('update:limit', val)
}
})
get() {
return props.limit;
},
set(val) {
emit('update:limit', val);
}
});
function handleSizeChange(val: number) {
emit('pagination', { page: currentPage, limit: val })
if (props.autoScroll) {
scrollTo(0, 800)
}
emit('pagination', { page: currentPage, limit: val });
if (props.autoScroll) {
scrollTo(0, 800);
}
}
function handleCurrentChange(val: number) {
currentPage.value = val
emit('pagination', { page: val, limit: props.limit })
if (props.autoScroll) {
scrollTo(0, 800)
}
currentPage.value = val;
emit('pagination', { page: val, limit: props.limit });
if (props.autoScroll) {
scrollTo(0, 800);
}
}
</script>
<style scoped>
.pagination-container {
background: #fff;
padding: 32px 16px;
background: #fff;
padding: 32px 16px;
}
.pagination-container.hidden {
display: none;
display: none;
}
</style>

View File

@@ -1,154 +1,163 @@
<template>
<div ref="rightPanel" :class="{ show: show }" class="rightPanel-container">
<div class="rightPanel-background" />
<div class="rightPanel">
<div class="handle-button" :style="{ 'top': buttonTop + 'px', 'background-color': theme }" @click="show = !show">
<Close style="width: 1em; height: 1em;vertical-align: middle " v-show="show" />
<Setting style="width:1em; height:1em;vertical-align: middle " v-show="!show" />
</div>
<div class="rightPanel-items">
<slot />
</div>
</div>
</div>
<div ref="rightPanel" :class="{ show: show }" class="rightPanel-container">
<div class="rightPanel-background" />
<div class="rightPanel">
<div
class="handle-button"
:style="{ top: buttonTop + 'px', 'background-color': theme }"
@click="show = !show"
>
<Close
style="width: 1em; height: 1em; vertical-align: middle"
v-show="show"
/>
<Setting
style="width: 1em; height: 1em; vertical-align: middle"
v-show="!show"
/>
</div>
<div class="rightPanel-items">
<slot />
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, onBeforeUnmount, onMounted, ref, watch } from "vue";
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
import { addClass, removeClass } from '@/utils/index'
import { addClass, removeClass } from '@/utils/index';
import useStore from "@/store";
import useStore from '@/store';
// 图标依赖
import { Close, Setting } from '@element-plus/icons-vue'
import { ElColorPicker } from "element-plus";
import { Close, Setting } from '@element-plus/icons-vue';
import { ElColorPicker } from 'element-plus';
const { setting } = useStore()
const { setting } = useStore();
const theme = computed(() => setting.theme)
const show = ref(false)
const theme = computed(() => setting.theme);
const show = ref(false);
defineProps({
buttonTop: {
default: 250,
type: Number
}
})
buttonTop: {
default: 250,
type: Number
}
});
watch(show, (value) => {
if (value) {
addEventClick()
}
if (value) {
addClass(document.body, 'showRightPanel')
} else {
removeClass(document.body, 'showRightPanel')
}
})
watch(show, value => {
if (value) {
addEventClick();
}
if (value) {
addClass(document.body, 'showRightPanel');
} else {
removeClass(document.body, 'showRightPanel');
}
});
function addEventClick() {
window.addEventListener('click', closeSidebar)
window.addEventListener('click', closeSidebar);
}
function closeSidebar(evt: any) {
// 主题选择点击不关闭
let parent = evt.target.closest('.theme-picker-dropdown');
if (parent) {
return;
}
// 主题选择点击不关闭
let parent = evt.target.closest('.theme-picker-dropdown')
if (parent) {
return
}
parent = evt.target.closest('.rightPanel')
if (!parent) {
show.value = false
window.removeEventListener('click', closeSidebar)
}
parent = evt.target.closest('.rightPanel');
if (!parent) {
show.value = false;
window.removeEventListener('click', closeSidebar);
}
}
const rightPanel = ref(ElColorPicker)
const rightPanel = ref(ElColorPicker);
function insertToBody() {
const elx = rightPanel.value as any
const body = document.querySelector('body') as any
body.insertBefore(elx, body.firstChild)
const elx = rightPanel.value as any;
const body = document.querySelector('body') as any;
body.insertBefore(elx, body.firstChild);
}
onMounted(() => {
insertToBody()
})
insertToBody();
});
onBeforeUnmount(() => {
const elx = rightPanel.value as any
elx.remove()
})
const elx = rightPanel.value as any;
elx.remove();
});
</script>
<style>
.showRightPanel {
overflow: hidden;
position: relative;
width: calc(100% - 15px);
overflow: hidden;
position: relative;
width: calc(100% - 15px);
}
</style>
<style lang="scss" scoped>
.rightPanel-background {
position: fixed;
top: 0;
left: 0;
opacity: 0;
transition: opacity .3s cubic-bezier(.7, .3, .1, 1);
background: rgba(0, 0, 0, .2);
z-index: -1;
position: fixed;
top: 0;
left: 0;
opacity: 0;
transition: opacity 0.3s cubic-bezier(0.7, 0.3, 0.1, 1);
background: rgba(0, 0, 0, 0.2);
z-index: -1;
}
.rightPanel {
width: 100%;
max-width: 260px;
height: 100vh;
position: fixed;
top: 0;
right: 0;
box-shadow: 0px 0px 15px 0px rgba(0, 0, 0, .05);
transition: all .25s cubic-bezier(.7, .3, .1, 1);
transform: translate(100%);
background: #fff;
z-index: 40000;
width: 100%;
max-width: 260px;
height: 100vh;
position: fixed;
top: 0;
right: 0;
box-shadow: 0px 0px 15px 0px rgba(0, 0, 0, 0.05);
transition: all 0.25s cubic-bezier(0.7, 0.3, 0.1, 1);
transform: translate(100%);
background: #fff;
z-index: 40000;
}
.show {
transition: all .3s cubic-bezier(.7, .3, .1, 1);
transition: all 0.3s cubic-bezier(0.7, 0.3, 0.1, 1);
.rightPanel-background {
z-index: 20000;
opacity: 1;
width: 100%;
height: 100%;
}
.rightPanel-background {
z-index: 20000;
opacity: 1;
width: 100%;
height: 100%;
}
.rightPanel {
transform: translate(0);
}
.rightPanel {
transform: translate(0);
}
}
.handle-button {
width: 48px;
height: 48px;
position: absolute;
left: -48px;
text-align: center;
font-size: 24px;
border-radius: 6px 0 0 6px !important;
z-index: 0;
pointer-events: auto;
cursor: pointer;
color: #fff;
line-height: 48px;
width: 48px;
height: 48px;
position: absolute;
left: -48px;
text-align: center;
font-size: 24px;
border-radius: 6px 0 0 6px !important;
z-index: 0;
pointer-events: auto;
cursor: pointer;
color: #fff;
line-height: 48px;
i {
font-size: 24px;
line-height: 48px;
}
i {
font-size: 24px;
line-height: 48px;
}
}
</style>

View File

@@ -1,12 +1,15 @@
<template>
<div>
<svg-icon :icon-class="isFullscreen ? 'exit-fullscreen' : 'fullscreen'" @click="toggle" />
</div>
<div>
<svg-icon
:icon-class="isFullscreen ? 'exit-fullscreen' : 'fullscreen'"
@click="toggle"
/>
</div>
</template>
<script setup lang="ts">
import { useFullscreen } from '@vueuse/core'
import SvgIcon from '@/components/SvgIcon/index.vue'
import { useFullscreen } from '@vueuse/core';
import SvgIcon from '@/components/SvgIcon/index.vue';
const { isFullscreen, toggle } = useFullscreen();
</script>
</script>

View File

@@ -1,43 +1,47 @@
<template>
<el-dropdown class="size-select" trigger="click" @command="handleSetSize">
<div class="size-select__icon">
<svg-icon class-name="size-icon" icon-class="size" />
</div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-for="item of sizeOptions" :key="item.value" :disabled="(size || 'default') == item.value"
:command="item.value">
{{ item.label }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-dropdown class="size-select" trigger="click" @command="handleSetSize">
<div class="size-select__icon">
<svg-icon class-name="size-icon" icon-class="size" />
</div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
v-for="item of sizeOptions"
:key="item.value"
:disabled="(size || 'default') == item.value"
:command="item.value"
>
{{ item.label }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
<script setup lang="ts">
import { ref, computed } from "vue";
import { ElMessage } from "element-plus";
import { ref, computed } from 'vue';
import { ElMessage } from 'element-plus';
import useStore from "@/store";
import SvgIcon from "@/components/SvgIcon/index.vue";
import useStore from '@/store';
import SvgIcon from '@/components/SvgIcon/index.vue';
const { app } = useStore();
const size = computed(() => app.size);
const sizeOptions = ref([
{ label: "默认", value: "default" },
{ label: "大型", value: "large" },
{ label: "小型", value: "small" },
{ label: '默认', value: 'default' },
{ label: '大型', value: 'large' },
{ label: '小型', value: 'small' }
]);
function handleSetSize(size: string) {
app.setSize(size);
ElMessage.success("切换布局大小成功");
app.setSize(size);
ElMessage.success('切换布局大小成功');
}
</script>
<style lang='scss' scoped>
<style lang="scss" scoped>
.size-select__icon {
line-height: 50px;
line-height: 50px;
}
</style>
</style>

View File

@@ -1,36 +1,36 @@
<template>
<svg aria-hidden="true" class="svg-icon">
<use :xlink:href="symbolId" :fill="color" />
</svg>
<svg aria-hidden="true" class="svg-icon">
<use :xlink:href="symbolId" :fill="color" />
</svg>
</template>
<script setup lang="ts">
import { computed } from 'vue';
const props=defineProps({
prefix: {
type: String,
default: 'icon',
},
iconClass: {
type: String,
required: false
},
color: {
type: String,
default: ''
}
})
const props = defineProps({
prefix: {
type: String,
default: 'icon'
},
iconClass: {
type: String,
required: false
},
color: {
type: String,
default: ''
}
});
const symbolId = computed(() => `#${props.prefix}-${props.iconClass}`);
</script>
<style scoped>
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
overflow: hidden;
fill: currentColor;
width: 1em;
height: 1em;
vertical-align: -0.15em;
overflow: hidden;
fill: currentColor;
}
</style>
</style>

View File

@@ -1,56 +1,67 @@
<template>
<el-color-picker
v-model="theme"
:predefine="['#409EFF', '#1890ff', '#304156','#212121','#11a983', '#13c2c2', '#6959CD', '#f5222d' ]"
class="theme-picker"
popper-class="theme-picker-dropdown"
/>
<el-color-picker
v-model="theme"
:predefine="[
'#409EFF',
'#1890ff',
'#304156',
'#212121',
'#11a983',
'#13c2c2',
'#6959CD',
'#f5222d'
]"
class="theme-picker"
popper-class="theme-picker-dropdown"
/>
</template>
<script setup lang="ts">
import {computed, watch} from "vue";
import useStore from "@/store";
import {localStorage} from "@/utils/storage";
import { computed, watch } from 'vue';
import useStore from '@/store';
import { localStorage } from '@/utils/storage';
// 参考连接:https://juejin.cn/post/7024025899813044232#heading-1
import {mix} from "@/utils";
import { mix } from '@/utils';
// 白色混合色
const mixWhite = "#ffffff";
const mixWhite = '#ffffff';
// 黑色混合色
const mixBlack = "#000000";
const mixBlack = '#000000';
const node = document.documentElement;
const {setting} =useStore()
const theme = computed(() => setting.theme)
const { setting } = useStore();
const theme = computed(() => setting.theme);
watch(theme, (color: string) => {
node.style.setProperty("--el-color-primary", color);
localStorage.set("theme", color)
node.style.setProperty('--el-color-primary', color);
localStorage.set('theme', color);
for (let i = 1; i < 10; i += 1) {
node.style.setProperty(`--el-color-primary-light-${i}`, mix(color, mixWhite, i * 0.1));
}
node.style.setProperty("--el-color-primary-dark", mix(color, mixBlack, 0.1));
localStorage.set("style", node.style.cssText);
})
for (let i = 1; i < 10; i += 1) {
node.style.setProperty(
`--el-color-primary-light-${i}`,
mix(color, mixWhite, i * 0.1)
);
}
node.style.setProperty('--el-color-primary-dark', mix(color, mixBlack, 0.1));
localStorage.set('style', node.style.cssText);
});
</script>
<style>
.theme-message,
.theme-picker-dropdown {
z-index: 99999 !important;
z-index: 99999 !important;
}
.theme-picker .el-color-picker__trigger {
height: 26px !important;
width: 26px !important;
padding: 2px;
height: 26px !important;
width: 26px !important;
padding: 2px;
}
.theme-picker-dropdown .el-color-dropdown__link-btn {
display: none;
display: none;
}
</style>

View File

@@ -1,67 +1,67 @@
<template>
<div>
<!-- 上传组件 -->
<el-upload
ref="singleUploadRef"
action=""
class="single-uploader"
:show-file-list="false"
:before-upload="handleBeforeUpload"
:http-request="uploadImage"
>
<img v-if="imgUrl" :src="imgUrl" class="single-uploader__image" />
<div>
<!-- 上传组件 -->
<el-upload
ref="singleUploadRef"
action=""
class="single-uploader"
:show-file-list="false"
:before-upload="handleBeforeUpload"
:http-request="uploadImage"
>
<img v-if="imgUrl" :src="imgUrl" class="single-uploader__image" />
<el-icon v-else class="single-uploader__plus">
<Plus />
</el-icon>
<el-icon v-else class="single-uploader__plus">
<Plus />
</el-icon>
<!-- 删除图标 -->
<el-icon
v-if="props.showClose && imgUrl"
class="single-uploader__remove"
@click.stop="handleRemove(imgUrl)"
>
<Close />
</el-icon>
</el-upload>
</div>
<!-- 删除图标 -->
<el-icon
v-if="props.showClose && imgUrl"
class="single-uploader__remove"
@click.stop="handleRemove(imgUrl)"
>
<Close />
</el-icon>
</el-upload>
</div>
</template>
<script setup lang="ts">
import { computed } from "vue";
import { Plus, Close } from "@element-plus/icons-vue";
import { computed } from 'vue';
import { Plus, Close } from '@element-plus/icons-vue';
import {
ElMessage,
ElUpload,
UploadRawFile,
UploadRequestOptions,
} from "element-plus";
import { uploadFile, deleteFile } from "@/api/system/file";
ElMessage,
ElUpload,
UploadRawFile,
UploadRequestOptions
} from 'element-plus';
import { uploadFile, deleteFile } from '@/api/system/file';
const emit = defineEmits(["update:modelValue"]);
const emit = defineEmits(['update:modelValue']);
const props = defineProps({
modelValue: {
type: String,
default: "",
},
/**
* 是否显示右上角的删除图片按钮
*/
showClose: {
type: Boolean,
default: false,
},
modelValue: {
type: String,
default: ''
},
/**
* 是否显示右上角的删除图片按钮
*/
showClose: {
type: Boolean,
default: false
}
});
const imgUrl = computed<string | undefined>({
get() {
return props.modelValue;
},
set(val) {
// imgUrl改变时触发修改父组件绑定的v-model的值
emit("update:modelValue", val);
},
get() {
return props.modelValue;
},
set(val) {
// imgUrl改变时触发修改父组件绑定的v-model的值
emit('update:modelValue', val);
}
});
/**
@@ -70,8 +70,8 @@ const imgUrl = computed<string | undefined>({
* @param params
*/
async function uploadImage(options: UploadRequestOptions): Promise<any> {
const response = await uploadFile(options.file);
imgUrl.value = response.data;
const response = await uploadFile(options.file);
imgUrl.value = response.data;
}
/**
@@ -80,60 +80,60 @@ async function uploadImage(options: UploadRequestOptions): Promise<any> {
* @param fileUrl
*/
function handleRemove(fileUrl?: string) {
if (fileUrl) {
deleteFile(fileUrl);
imgUrl.value = undefined; // 这里会触发imgUrl的computed的set方法
}
if (fileUrl) {
deleteFile(fileUrl);
imgUrl.value = undefined; // 这里会触发imgUrl的computed的set方法
}
}
/**
* 在 before-upload 钩子中限制用户上传文件的格式和大小
*/
function handleBeforeUpload(file: UploadRawFile) {
// const isJPG = file.type === "image/jpeg";
const isLt2M = file.size / 1024 / 1024 < 2;
// const isJPG = file.type === "image/jpeg";
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
ElMessage.warning("上传图片不能大于2M");
}
return true;
if (!isLt2M) {
ElMessage.warning('上传图片不能大于2M');
}
return true;
}
</script>
<style lang="scss" scoped>
.single-uploader {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
transition: var(--el-transition-duration-fast);
text-align: center;
&:hover {
border-color: var(--el-color-primary);
}
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
transition: var(--el-transition-duration-fast);
text-align: center;
&:hover {
border-color: var(--el-color-primary);
}
&__image {
width: 146px;
height: 146px;
display: block;
}
&__image {
width: 146px;
height: 146px;
display: block;
}
&__plus {
width: 146px;
height: 157px;
font-size: 28px;
color: #8c939d;
text-align: center;
}
&__plus {
width: 146px;
height: 157px;
font-size: 28px;
color: #8c939d;
text-align: center;
}
&__remove {
font-size: 12px;
color: #ff4d51 !important;
margin-top: 0px !important;
position: absolute;
right: 0;
top: 0;
color: #409eff;
}
&__remove {
font-size: 12px;
color: #ff4d51 !important;
margin-top: 0px !important;
position: absolute;
right: 0;
top: 0;
color: #409eff;
}
}
</style>

View File

@@ -1,70 +1,79 @@
<template>
<div style="border: 1px solid #ccc">
<!-- 工具栏 -->
<Toolbar :editor="editorRef" :defaultConfig="toolbarConfig" style="border-bottom: 1px solid #ccc" :mode="mode" />
<!-- 编辑器 -->
<Editor :defaultConfig="editorConfig" v-model="defaultHtml" @onChange="handleChange"
style="height: 500px; overflow-y: hidden;" :mode="mode" @onCreated="handleCreated" />
</div>
<div style="border: 1px solid #ccc">
<!-- 工具栏 -->
<Toolbar
:editor="editorRef"
:defaultConfig="toolbarConfig"
style="border-bottom: 1px solid #ccc"
:mode="mode"
/>
<!-- 编辑器 -->
<Editor
:defaultConfig="editorConfig"
v-model="defaultHtml"
@onChange="handleChange"
style="height: 500px; overflow-y: hidden"
:mode="mode"
@onCreated="handleCreated"
/>
</div>
</template>
<script setup lang="ts">
import { onBeforeUnmount, shallowRef, reactive, toRefs} from 'vue'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import { onBeforeUnmount, shallowRef, reactive, toRefs } from 'vue';
import { Editor, Toolbar } from '@wangeditor/editor-for-vue';
// API 引用
import { uploadFile } from "@/api/system/file";
import { uploadFile } from '@/api/system/file';
const props = defineProps({
modelValue: {
type: [String],
default: ''
},
})
modelValue: {
type: [String],
default: ''
}
});
const emit = defineEmits(['update:modelValue']);
// 编辑器实例,必须用 shallowRef
const editorRef = shallowRef()
const editorRef = shallowRef();
const state = reactive({
toolbarConfig: {},
editorConfig: {
placeholder: '请输入内容...',
MENU_CONF: {
uploadImage: {
// 自定义图片上传
async customUpload(file: any, insertFn: any) {
uploadFile(file).then(response => {
const url = response.data
insertFn(url)
})
}
}
}
},
defaultHtml: props.modelValue,
mode: 'default'
})
toolbarConfig: {},
editorConfig: {
placeholder: '请输入内容...',
MENU_CONF: {
uploadImage: {
// 自定义图片上传
async customUpload(file: any, insertFn: any) {
uploadFile(file).then(response => {
const url = response.data;
insertFn(url);
});
}
}
}
},
defaultHtml: props.modelValue,
mode: 'default'
});
const { toolbarConfig, editorConfig, defaultHtml, mode } = toRefs(state)
const { toolbarConfig, editorConfig, defaultHtml, mode } = toRefs(state);
const handleCreated = (editor: any) => {
editorRef.value = editor // 记录 editor 实例,重要!
}
editorRef.value = editor; // 记录 editor 实例,重要!
};
function handleChange(editor: any) {
emit('update:modelValue', editor.getHtml())
emit('update:modelValue', editor.getHtml());
}
// 组件销毁时,也及时销毁编辑器
onBeforeUnmount(() => {
const editor = editorRef.value
if (editor == null) return
editor.destroy()
})
const editor = editorRef.value;
if (editor == null) return;
editor.destroy();
});
</script>
<style src="@wangeditor/editor/dist/css/style.css">
</style>
<style src="@wangeditor/editor/dist/css/style.css"></style>