xiaoxian521
3 years ago
55 changed files with 454 additions and 853 deletions
-
2LICENSE
-
9README.en-US.md
-
8README.md
-
102build/plugins.ts
-
1index.html
-
5mock/asyncRoutes.ts
-
21package.json
-
242pnpm-lock.yaml
-
18public/iconfont.css
-
BINsrc/assets/car.png
-
0src/assets/login/illustration.svg
-
1src/assets/login/illustration0.svg
-
1src/assets/login/illustration1.svg
-
1src/assets/login/illustration3.svg
-
1src/assets/login/illustration4.svg
-
1src/assets/login/illustration5.svg
-
1src/assets/login/illustration6.svg
-
149src/components/ReIcon/index.ts
-
97src/components/ReIcon/src/Icon.vue
-
39src/components/ReIcon/src/hooks.ts
-
48src/components/ReIcon/src/iconfont.ts
-
25src/components/ReIcon/src/iconifyIconOffline.ts
-
8src/components/ReIcon/src/iconifyIconOnline.ts
-
6src/layout/components/navbar.vue
-
6src/layout/components/notice/data.ts
-
10src/layout/components/screenfull/index.vue
-
10src/layout/components/setting/index.vue
-
17src/layout/components/sidebar/horizontal.vue
-
13src/layout/components/sidebar/logo.vue
-
19src/layout/components/sidebar/sidebarItem.vue
-
3src/layout/components/sidebar/vertical.vue
-
28src/layout/components/tag/index.scss
-
27src/layout/components/tag/index.vue
-
4src/layout/theme/element-plus.ts
-
3src/layout/types.ts
-
15src/main.ts
-
26src/plugins/element-plus/index.ts
-
21src/plugins/fontawesome/index.ts
-
17src/plugins/i18n/en/menus.ts
-
17src/plugins/i18n/zh-CN/menus.ts
-
7src/router/modules/error.ts
-
2src/router/modules/externalLink.ts
-
4src/router/modules/home.ts
-
2src/router/utils.ts
-
3src/store/modules/multiTags.ts
-
3src/store/modules/user.ts
-
4src/style/login.css
-
21src/utils/algorithm/index.ts
-
54src/utils/loaders/index.ts
-
35src/utils/resize/index.ts
-
3src/utils/storage/responsive.ts
-
38src/views/login.vue
-
1types/global.d.ts
-
106vite.config.ts
@ -0,0 +1,102 @@ |
|||
import vue from "@vitejs/plugin-vue"; |
|||
import svgLoader from "vite-svg-loader"; |
|||
import legacy from "@vitejs/plugin-legacy"; |
|||
import vueJsx from "@vitejs/plugin-vue-jsx"; |
|||
import WindiCSS from "vite-plugin-windicss"; |
|||
import { viteMockServe } from "vite-plugin-mock"; |
|||
import liveReload from "vite-plugin-live-reload"; |
|||
import ElementPlus from "unplugin-element-plus/vite"; |
|||
import removeConsole from "vite-plugin-remove-console"; |
|||
import themePreprocessorPlugin from "@zougt/vite-plugin-theme-preprocessor"; |
|||
|
|||
export function getPluginsList(command, VITE_LEGACY) { |
|||
const prodMock = true; |
|||
return [ |
|||
vue(), |
|||
// jsx、tsx语法支持
|
|||
vueJsx(), |
|||
WindiCSS(), |
|||
// 线上环境删除console
|
|||
removeConsole(), |
|||
// 修改layout文件夹下的文件时自动重载浏览器 解决 https://github.com/xiaoxian521/vue-pure-admin/issues/170
|
|||
liveReload(["src/layout/**/*", "src/router/**/*"]), |
|||
// 自定义主题
|
|||
themePreprocessorPlugin({ |
|||
scss: { |
|||
multipleScopeVars: [ |
|||
{ |
|||
scopeName: "layout-theme-default", |
|||
path: "src/layout/theme/default-vars.scss" |
|||
}, |
|||
{ |
|||
scopeName: "layout-theme-light", |
|||
path: "src/layout/theme/light-vars.scss" |
|||
}, |
|||
{ |
|||
scopeName: "layout-theme-dusk", |
|||
path: "src/layout/theme/dusk-vars.scss" |
|||
}, |
|||
{ |
|||
scopeName: "layout-theme-volcano", |
|||
path: "src/layout/theme/volcano-vars.scss" |
|||
}, |
|||
{ |
|||
scopeName: "layout-theme-yellow", |
|||
path: "src/layout/theme/yellow-vars.scss" |
|||
}, |
|||
{ |
|||
scopeName: "layout-theme-mingQing", |
|||
path: "src/layout/theme/mingQing-vars.scss" |
|||
}, |
|||
{ |
|||
scopeName: "layout-theme-auroraGreen", |
|||
path: "src/layout/theme/auroraGreen-vars.scss" |
|||
}, |
|||
{ |
|||
scopeName: "layout-theme-pink", |
|||
path: "src/layout/theme/pink-vars.scss" |
|||
}, |
|||
{ |
|||
scopeName: "layout-theme-saucePurple", |
|||
path: "src/layout/theme/saucePurple-vars.scss" |
|||
} |
|||
], |
|||
// 默认取 multipleScopeVars[0].scopeName
|
|||
defaultScopeName: "", |
|||
// 在生产模式是否抽取独立的主题css文件,extract为true以下属性有效
|
|||
extract: true, |
|||
// 独立主题css文件的输出路径,默认取 viteConfig.build.assetsDir 相对于 (viteConfig.build.outDir)
|
|||
outputDir: "", |
|||
// 会选取defaultScopeName对应的主题css文件在html添加link
|
|||
themeLinkTagId: "head", |
|||
// "head"||"head-prepend" || "body" ||"body-prepend"
|
|||
themeLinkTagInjectTo: "head", |
|||
// 是否对抽取的css文件内对应scopeName的权重类名移除
|
|||
removeCssScopeName: false, |
|||
// 可以自定义css文件名称的函数
|
|||
customThemeCssFileName: scopeName => scopeName |
|||
} |
|||
}), |
|||
// svg组件化支持
|
|||
svgLoader(), |
|||
ElementPlus({}), |
|||
// mock支持
|
|||
viteMockServe({ |
|||
mockPath: "mock", |
|||
localEnabled: command === "serve", |
|||
prodEnabled: command !== "serve" && prodMock, |
|||
injectCode: `
|
|||
import { setupProdMockServer } from './mockProdServer'; |
|||
setupProdMockServer(); |
|||
`,
|
|||
logger: true |
|||
}), |
|||
// 是否为打包后的文件提供传统浏览器兼容性支持
|
|||
VITE_LEGACY |
|||
? legacy({ |
|||
targets: ["ie >= 11"], |
|||
additionalLegacyPolyfills: ["regenerator-runtime/runtime"] |
|||
}) |
|||
: null |
|||
]; |
|||
} |
@ -1,18 +0,0 @@ |
|||
@font-face { |
|||
font-family: "iconfont"; /* project id 1098500 */ |
|||
src: url("//at.alicdn.com/t/font_1098500_3d6un9zwltz.eot"); |
|||
src: url("//at.alicdn.com/t/font_1098500_3d6un9zwltz.eot?#iefix") |
|||
format("embedded-opentype"), |
|||
url("//at.alicdn.com/t/font_1098500_3d6un9zwltz.woff2") format("woff2"), |
|||
url("//at.alicdn.com/t/font_1098500_3d6un9zwltz.woff") format("woff"), |
|||
url("//at.alicdn.com/t/font_1098500_3d6un9zwltz.ttf") format("truetype"), |
|||
url("//at.alicdn.com/t/font_1098500_3d6un9zwltz.svg#iconfont") format("svg"); |
|||
} |
|||
|
|||
.iconfont { |
|||
font-family: "iconfont" !important; |
|||
font-size: 16px; |
|||
font-style: normal; |
|||
-webkit-font-smoothing: antialiased; |
|||
-moz-osx-font-smoothing: grayscale; |
|||
} |
Before Width: 36 | Height: 20 | Size: 1.4 KiB |
1
src/assets/login/illustration0.svg
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
1
src/assets/login/illustration1.svg
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
1
src/assets/login/illustration3.svg
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
1
src/assets/login/illustration4.svg
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
1
src/assets/login/illustration5.svg
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
1
src/assets/login/illustration6.svg
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -1,154 +1,13 @@ |
|||
import { h, App, defineComponent } from "vue"; |
|||
import icon from "./src/Icon.vue"; |
|||
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"; |
|||
import iconifyIconOffline from "./src/iconifyIconOffline"; |
|||
import iconifyIconOnline from "./src/iconifyIconOnline"; |
|||
|
|||
/** |
|||
* find icon component |
|||
* @param icon icon图标 |
|||
* @returns component |
|||
*/ |
|||
export function findIconReg(icon: string) { |
|||
// fontawesome4
|
|||
const fa4Reg = /^fa-/; |
|||
// fontawesome5+
|
|||
const fa5Reg = /^FA-/; |
|||
// iconfont
|
|||
const iFReg = /^IF-/; |
|||
// remixicon
|
|||
const riReg = /^RI-/; |
|||
// typeof icon === "function" 属于SVG
|
|||
if (fa5Reg.test(icon)) { |
|||
const text = icon.split(fa5Reg)[1]; |
|||
return findIcon( |
|||
text.slice(0, text.indexOf(" ") == -1 ? text.length : text.indexOf(" ")), |
|||
"FA", |
|||
text.slice(text.indexOf(" ") + 1, text.length) |
|||
); |
|||
} else if (fa4Reg.test(icon)) { |
|||
return findIcon(icon.split(fa4Reg)[1], "fa"); |
|||
} else if (iFReg.test(icon)) { |
|||
return findIcon(icon.split(iFReg)[1], "IF"); |
|||
} else if (typeof icon === "function") { |
|||
return findIcon(icon, "SVG"); |
|||
} else if (riReg.test(icon)) { |
|||
return findIcon(icon.split(riReg)[1], "RI"); |
|||
} else { |
|||
return findIcon(icon, "EL"); |
|||
} |
|||
} |
|||
|
|||
// 支持fontawesome、iconfont、remixicon、element-plus/icons、自定义svg
|
|||
export function findIcon(icon: String, type = "EL", property?: string) { |
|||
if (type === "FA") { |
|||
return defineComponent({ |
|||
name: "FaIcon", |
|||
data() { |
|||
return { icon, property }; |
|||
}, |
|||
components: { FontAwesomeIcon }, |
|||
render() { |
|||
return h( |
|||
FontAwesomeIcon, |
|||
{ |
|||
icon: `${this.icon}`, |
|||
[property]: true |
|||
}, |
|||
{ |
|||
default: () => [] |
|||
} |
|||
); |
|||
} |
|||
}); |
|||
} else if (type === "fa") { |
|||
return defineComponent({ |
|||
name: "faIcon", |
|||
data() { |
|||
return { icon: `fa ${icon}` }; |
|||
}, |
|||
render() { |
|||
return h( |
|||
"i", |
|||
{ |
|||
class: `${this.icon}` |
|||
}, |
|||
{ |
|||
default: () => [] |
|||
} |
|||
); |
|||
} |
|||
}); |
|||
} else if (type === "IF") { |
|||
return defineComponent({ |
|||
name: "IfIcon", |
|||
data() { |
|||
return { icon: `iconfont ${icon}` }; |
|||
}, |
|||
render() { |
|||
return h( |
|||
"i", |
|||
{ |
|||
class: `${this.icon}` |
|||
}, |
|||
{ |
|||
default: () => [] |
|||
} |
|||
); |
|||
} |
|||
}); |
|||
} else if (type === "RI") { |
|||
return defineComponent({ |
|||
name: "RiIcon", |
|||
data() { |
|||
return { icon: `ri-${icon}` }; |
|||
}, |
|||
render() { |
|||
return h( |
|||
"i", |
|||
{ |
|||
class: `${this.icon}` |
|||
}, |
|||
{ |
|||
default: () => [] |
|||
} |
|||
); |
|||
} |
|||
}); |
|||
} else if (type === "EL") { |
|||
return defineComponent({ |
|||
name: "ElIcon", |
|||
data() { |
|||
return { icon }; |
|||
}, |
|||
render() { |
|||
return h( |
|||
IconifyIconOffline, |
|||
{ |
|||
icon: `${this.icon}` |
|||
}, |
|||
{ |
|||
default: () => [] |
|||
} |
|||
); |
|||
} |
|||
}); |
|||
} else if (type === "SVG") { |
|||
return icon; |
|||
} |
|||
} |
|||
|
|||
export const Icon = Object.assign(icon, { |
|||
install(app: App) { |
|||
app.component(icon.name, icon); |
|||
} |
|||
}); |
|||
import fontIcon from "./src/iconfont"; |
|||
|
|||
export const IconifyIconOffline = iconifyIconOffline; |
|||
export const IconifyIconOnline = iconifyIconOnline; |
|||
export const FontIcon = fontIcon; |
|||
|
|||
export default { |
|||
Icon, |
|||
IconifyIconOffline, |
|||
IconifyIconOnline |
|||
IconifyIconOnline, |
|||
FontIcon |
|||
}; |
@ -1,97 +0,0 @@ |
|||
<script lang="ts"> |
|||
export default { |
|||
name: "Icon" |
|||
}; |
|||
</script> |
|||
|
|||
<script setup lang="ts"> |
|||
import { ref, computed } from "vue"; |
|||
|
|||
const props = defineProps({ |
|||
content: { |
|||
type: String, |
|||
default: "" |
|||
}, |
|||
size: { |
|||
type: Number, |
|||
default: 18 |
|||
}, |
|||
width: { |
|||
type: Number, |
|||
default: 20 |
|||
}, |
|||
height: { |
|||
type: Number, |
|||
default: 20 |
|||
}, |
|||
color: { |
|||
type: String, |
|||
default: "" |
|||
}, |
|||
svg: { |
|||
type: Boolean, |
|||
default: false |
|||
} |
|||
}); |
|||
|
|||
const emit = defineEmits<{ |
|||
(e: "click"): void; |
|||
}>(); |
|||
|
|||
let text = ref(""); |
|||
|
|||
let className = computed(() => { |
|||
if (props.content.indexOf("fa-") > -1) { |
|||
return props.content.indexOf("fa ") === 0 |
|||
? props.content |
|||
: ["fa", props.content]; |
|||
} else if (props.content.indexOf("el-icon-") > -1) { |
|||
return props.content; |
|||
} else if (props.content.indexOf("#") > -1) { |
|||
// eslint-disable-next-line vue/no-side-effects-in-computed-properties |
|||
text.value = props.content; |
|||
return "iconfont"; |
|||
} else { |
|||
// eslint-disable-next-line vue/no-side-effects-in-computed-properties |
|||
text.value = props.content; |
|||
return ""; |
|||
} |
|||
}); |
|||
|
|||
let iconStyle = computed(() => { |
|||
return ( |
|||
"font-size: " + |
|||
props.size + |
|||
"px; color: " + |
|||
props.color + |
|||
"; width: " + |
|||
props.width + |
|||
"px; height: " + |
|||
props.height + |
|||
"px; font-style: normal;" |
|||
); |
|||
}); |
|||
|
|||
const clickHandle = () => { |
|||
emit("click"); |
|||
}; |
|||
</script> |
|||
|
|||
<template> |
|||
<i |
|||
v-if="!props.svg" |
|||
:class="className" |
|||
:style="iconStyle" |
|||
v-html="text" |
|||
@click="clickHandle" |
|||
></i> |
|||
<svg |
|||
class="icon-svg" |
|||
v-if="props.svg" |
|||
aria-hidden="true" |
|||
:style="iconStyle" |
|||
@click="clickHandle" |
|||
> |
|||
<use :xlink:href="`#${props.content}`" /> |
|||
</svg> |
|||
</template> |
@ -0,0 +1,39 @@ |
|||
import { h, defineComponent, Component } from "vue"; |
|||
import { IconifyIconOffline, FontIcon } from "../index"; |
|||
|
|||
// 支持fontawesome4、5+、iconfont、remixicon、element-plus的icons、自定义svg
|
|||
export function useRenderIcon(icon: string): Component { |
|||
// iconfont
|
|||
const ifReg = /^IF-/; |
|||
// typeof icon === "function" 属于SVG
|
|||
if (ifReg.test(icon)) { |
|||
// iconfont
|
|||
const name = icon.split(ifReg)[1]; |
|||
const iconName = name.slice( |
|||
0, |
|||
name.indexOf(" ") == -1 ? name.length : name.indexOf(" ") |
|||
); |
|||
const iconType = name.slice(name.indexOf(" ") + 1, name.length); |
|||
return defineComponent({ |
|||
name: "FontIcon", |
|||
render() { |
|||
return h(FontIcon, { |
|||
icon: iconName, |
|||
iconType |
|||
}); |
|||
} |
|||
}); |
|||
} else if (typeof icon === "function") { |
|||
// svg
|
|||
return icon; |
|||
} else { |
|||
return defineComponent({ |
|||
name: "Icon", |
|||
render() { |
|||
return h(IconifyIconOffline, { |
|||
icon: icon |
|||
}); |
|||
} |
|||
}); |
|||
} |
|||
} |
@ -0,0 +1,48 @@ |
|||
import { h, defineComponent } from "vue"; |
|||
|
|||
// 封装iconfont组件,默认`font-class`引用模式,支持`unicode`引用、`font-class`引用、`symbol`引用 (https://www.iconfont.cn/help/detail?spm=a313x.7781069.1998910419.20&helptype=code)
|
|||
export default defineComponent({ |
|||
name: "fontIcon", |
|||
props: { |
|||
icon: { |
|||
type: String, |
|||
default: "" |
|||
} |
|||
}, |
|||
render() { |
|||
const attrs = this.$attrs; |
|||
if (Object.keys(attrs).includes("uni") || attrs?.iconType === "uni") { |
|||
return h( |
|||
"i", |
|||
{ |
|||
class: "iconfont", |
|||
...attrs |
|||
}, |
|||
this.icon |
|||
); |
|||
} else if ( |
|||
Object.keys(attrs).includes("svg") || |
|||
attrs?.iconType === "svg" |
|||
) { |
|||
return h( |
|||
"svg", |
|||
{ |
|||
class: "icon-svg", |
|||
"aria-hidden": true |
|||
}, |
|||
{ |
|||
default: () => [ |
|||
h("use", { |
|||
"xlink:href": `#${this.icon}` |
|||
}) |
|||
] |
|||
} |
|||
); |
|||
} else { |
|||
return h("i", { |
|||
class: `iconfont ${this.icon}`, |
|||
...attrs |
|||
}); |
|||
} |
|||
} |
|||
}); |
@ -1,21 +0,0 @@ |
|||
/** 兼容fontawesome4和5版本 |
|||
* 4版本: www.fontawesome.com.cn/faicons/ |
|||
* 5版本:https://fontawesome.com/v5.15/icons?d=gallery&p=2&m=free
|
|||
* https://github.com/FortAwesome/vue-fontawesome
|
|||
*/ |
|||
import { App } from "vue"; |
|||
import "font-awesome/css/font-awesome.css"; |
|||
import { library } from "@fortawesome/fontawesome-svg-core"; |
|||
import { |
|||
faUserSecret, |
|||
faCoffee, |
|||
faSpinner |
|||
} from "@fortawesome/free-solid-svg-icons"; |
|||
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"; |
|||
// github.com/Remix-Design/RemixIcon/blob/master/README_CN.md#%E5%AE%89%E8%A3%85%E5%BC%95%E5%85%A5
|
|||
import "remixicon/fonts/remixicon.css"; |
|||
|
|||
export function useFontawesome(app: App) { |
|||
library.add(faUserSecret, faCoffee, faSpinner); |
|||
app.component("font-awesome-icon", FontAwesomeIcon); |
|||
} |
@ -1,21 +0,0 @@ |
|||
interface ProxyAlgorithm { |
|||
increaseIndexes<T>(val: Array<T>): Array<T>; |
|||
} |
|||
|
|||
class algorithmProxy implements ProxyAlgorithm { |
|||
constructor() {} |
|||
|
|||
// 数组每一项添加索引字段
|
|||
public increaseIndexes<T>(val: Array<T>): Array<T> { |
|||
return Object.keys(val) |
|||
.map(v => { |
|||
return { |
|||
...val[v], |
|||
key: v |
|||
}; |
|||
}) |
|||
.filter(v => v.meta && v.meta.showLink); |
|||
} |
|||
} |
|||
|
|||
export const algorithm = new algorithmProxy(); |
@ -1,54 +0,0 @@ |
|||
interface ProxyLoader { |
|||
loadCss(src: string): any; |
|||
loadScript(src: string): Promise<any>; |
|||
loadScriptConcurrent(src: Array<string>): Promise<any>; |
|||
} |
|||
|
|||
class loaderProxy implements ProxyLoader { |
|||
constructor() {} |
|||
|
|||
protected scriptLoaderCache: Array<string> = []; |
|||
|
|||
public loadCss = (src: string): any => { |
|||
const element: HTMLLinkElement = document.createElement("link"); |
|||
element.rel = "stylesheet"; |
|||
element.href = src; |
|||
document.body.appendChild(element); |
|||
}; |
|||
|
|||
public loadScript = async (src: string): Promise<any> => { |
|||
if (this.scriptLoaderCache.includes(src)) { |
|||
return src; |
|||
} else { |
|||
const element: HTMLScriptElement = document.createElement("script"); |
|||
element.src = src; |
|||
document.body.appendChild(element); |
|||
element.onload = () => { |
|||
return this.scriptLoaderCache.push(src); |
|||
}; |
|||
} |
|||
}; |
|||
|
|||
public loadScriptConcurrent = async ( |
|||
srcList: Array<string> |
|||
): Promise<any> => { |
|||
if (Array.isArray(srcList)) { |
|||
const len: number = srcList.length; |
|||
if (len > 0) { |
|||
let count = 0; |
|||
srcList.map(src => { |
|||
if (src) { |
|||
this.loadScript(src).then(() => { |
|||
count++; |
|||
if (count === len) { |
|||
return; |
|||
} |
|||
}); |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
}; |
|||
} |
|||
|
|||
export const loader = new loaderProxy(); |
@ -1,35 +0,0 @@ |
|||
import ResizeObserver from "resize-observer-polyfill"; |
|||
|
|||
const isServer = typeof window === "undefined"; |
|||
|
|||
const resizeHandler = (entries: any[]): void => { |
|||
for (const entry of entries) { |
|||
const listeners = entry.target.__resizeListeners__ || []; |
|||
if (listeners.length) { |
|||
listeners.forEach((fn: () => any) => { |
|||
fn(); |
|||
}); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
export const addResizeListener = (element: any, fn: () => any): any => { |
|||
if (isServer) return; |
|||
if (!element.__resizeListeners__) { |
|||
element.__resizeListeners__ = []; |
|||
element.__ro__ = new ResizeObserver(resizeHandler); |
|||
element.__ro__.observe(element); |
|||
} |
|||
element.__resizeListeners__.push(fn); |
|||
}; |
|||
|
|||
export const removeResizeListener = (element: any, fn: () => any): any => { |
|||
if (!element || !element.__resizeListeners__) return; |
|||
element.__resizeListeners__.splice( |
|||
element.__resizeListeners__.indexOf(fn), |
|||
1 |
|||
); |
|||
if (!element.__resizeListeners__.length) { |
|||
element.__ro__.disconnect(); |
|||
} |
|||
}; |
Write
Preview
Loading…
Cancel
Save
Reference in new issue