feat:更新依赖组件ICON配置

This commit is contained in:
fangyunong 2025-07-06 11:41:09 +08:00
parent 5acb144b21
commit cde38eaa17
20 changed files with 2328 additions and 139 deletions

View File

@ -32,6 +32,7 @@
"@vue/babel-plugin-jsx": "^1.4.0",
"typescript": "^5.2.2",
"vite": "^5.3.4",
"vite-plugin-svg-icons": "^2.0.1",
"vue-tsc": "^2.0.24"
}
}

1994
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

View File

@ -0,0 +1,177 @@
<template>
<div class="empty-state" :class="[`empty-state--${type}`]">
<div class="empty-state__icon" :class="`empty-state__icon--${type}`">
<slot name="icon">
<img v-if="type === 'no-data'" src="@/assets/images/暂无项目.png">
<img v-else-if="type === 'no-permission'" src="@/assets/images/暂无权限.png">
<img v-else src="@/assets/images/空空如也.png">
</slot>
</div>
<h3 class="empty-state__title">{{ title }}</h3>
<p class="empty-state__desc" v-if="description">{{ description }}</p>
<div class="empty-state__action" v-if="$slots.default">
<slot></slot>
</div>
</div>
</template>
<script lang="ts">
type EmptyStateType = 'no-data' | 'no-permission' | 'not-opened'
export default defineComponent({
name: 'ElzEmpty',
label: '空状态',
props: {
type: {
type: String as () => EmptyStateType,
default: 'no-data',
validator: (value: string): value is EmptyStateType =>
['no-data', 'no-permission', 'not-opened'].includes(value as EmptyStateType)
},
title: {
type: String,
default: ''
},
description: {
type: String,
default: ''
}
},
emits: ['update:title', 'update:description'],
setup(props, { emit }) {
// const iconComponent = computed(() => {
// const icons: Record<EmptyStateType, string> = {
// 'no-data': 'el-icon-data-analysis',
// 'no-permission': 'el-icon-thumb',
// 'not-opened': 'el-icon-receiving'
// }
// return icons[props.type] || 'el-icon-receiving'
// })
const defaultTitles: Record<EmptyStateType, string> = {
'no-data': '暂无数据',
'no-permission': '无访问权限',
'not-opened': '暂未开通'
}
const defaultDescriptions: Record<EmptyStateType, string> = {
'no-data': '当前没有找到相关数据',
'no-permission': '您没有权限访问此内容',
'not-opened': '该功能暂未开通,敬请期待'
}
// Set default title and description if not provided
if (!props.title) {
emit('update:title', defaultTitles[props.type])
}
if (!props.description) {
emit('update:description', defaultDescriptions[props.type])
}
}
})
</script>
<style lang="scss" scoped>
.empty-state {
$block: &;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
padding: 40px 20px;
animation: fadeIn 0.5s ease-in-out;
img{
width:100%;
}
&__icon {
width: 120px;
height: 120px;
margin-bottom: 24px;
animation: bounceIn 0.8s ease-in-out;
svg {
width: 100%;
height: 100%;
}
&--no-data {
color: #909399;
}
&--no-permission {
color: #f56c6c;
}
&--not-opened {
color: #e6a23c;
}
}
&__title {
margin: 0 0 12px;
font-size: 18px;
font-weight: 500;
color: #303133;
animation: slideInUp 0.5s ease-in-out;
}
&__desc {
margin: 0;
font-size: 14px;
color: #606266;
line-height: 1.5;
animation: slideInUp 0.6s ease-in-out;
}
&__action {
margin-top: 24px;
animation: slideInUp 0.7s ease-in-out;
}
//
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes bounceIn {
0% {
opacity: 0;
transform: scale(0.3);
}
50% {
opacity: 1;
transform: scale(1.05);
}
70% {
transform: scale(0.9);
}
100% {
transform: scale(1);
}
}
@keyframes slideInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
}
</style>

View File

@ -0,0 +1,2 @@
这里封装的是零枢公共UI组件库基于vue3 + naive-ui后面也许会迁移到私服npm包上。
如果要封装请以Ls开头

View File

@ -0,0 +1,27 @@
<template>
<header>
<svg-icon icon-class="mainproject" width="16" height="16" color="#3D8EFF"></svg-icon>
<p class="title">{{ title }}</p>
</header>
</template>
<script setup lang='ts'>
defineProps<{
'title':String
}>()
</script>
<style scoped lang='scss'>
header {
height: 40px;
display: flex;
align-items: center;
padding: 0 16px;
border-bottom: 1px solid $dashLineColor;
gap: 4px;
.title {
color: $titleTextColor;
font-weight: bold;
}
}
</style>

View File

@ -0,0 +1,52 @@
<template>
<svg
class="svg-icon"
:style="svgStyle"
aria-hidden="true"
v-on="$attrs"
>
<use :xlink:href="iconName" />
</svg>
</template>
<script setup>
import { computed } from 'vue';
const props = defineProps({
// SVG assets/icons
iconClass: {
type: String,
required: true
},
//
width: {
type: [String, Number],
default: '1em'
},
//
height: {
type: [String, Number],
default: '1em'
},
//
color: {
type: String,
default: 'currentColor'
}
});
const iconName = computed(() => `#icon-${props.iconClass}`);
const svgStyle = computed(() => ({
width: typeof props.width === 'number' ? `${props.width}px` : props.width,
height: typeof props.height === 'number' ? `${props.height}px` : props.height,
fill: props.color
}));
</script>
<style lang="scss" scoped>
.svg-icon {
display: inline-block;
vertical-align: middle;
overflow: hidden;
}
</style>

View File

@ -10,13 +10,19 @@ import router from "@/router";
import naive from 'naive-ui';
import 'vfonts/Lato.css'// 通用字体
import 'vfonts/FiraCode.css'// 等宽字体
import 'virtual:svg-icons-register'; //SVG精灵图册
// 登录授权相关
import { createAuth0 } from '@auth0/auth0-vue';
import { createApp } from 'vue'
import App from './App.vue';
// 全局组件
import SvgIcon from '@/components/SvgIcon.vue';
import LsComponent from './plugins/globalLsComponents'
const app = createApp(App);
app.component('svg-icon',SvgIcon); //图标组件
app.use(LsComponent); //零枢封装组件
app.use(
createAuth0({
domain: DO_MAIN,

View File

@ -0,0 +1,20 @@
// src/plugins/globalComponents.ts
import type { App } from 'vue'
export default {
install(app: App) {
// 使用 import.meta.glob 动态导入所有 Ls 开头的组件
const components = import.meta.glob('../components/Ls-UI/Ls*.vue', { eager: true })
for (const path in components) {
const component = components[path] as any
// 获取组件名称(去掉路径和扩展名,只保留文件名)
const componentName = path
.split('/')
.pop()
?.replace(/\.\w+$/, '') || ''
// 注册组件
app.component(componentName, component.default || component)
}
}
}

View File

@ -2,7 +2,7 @@
<div class="header__item">
<header>
<div class="icon">
<img src="@/assets/icons/order.svg">
<svg-icon icon-class="order" width="16" height="16" color="#fff"></svg-icon>
</div>
<p class="title">今日新增订单</p>
</header>
@ -18,7 +18,7 @@
<div class="header__item">
<header>
<div class="icon">
<img src="@/assets/icons/order.svg">
<svg-icon icon-class="order" width="16" height="16" color="#fff"></svg-icon>
</div>
<p class="title">今日新增陪玩</p>
</header>
@ -34,7 +34,7 @@
<div class="header__item">
<header>
<div class="icon">
<img src="@/assets/icons/money.svg">
<svg-icon icon-class="money" width="16" height="16" color="#fff"></svg-icon>
</div>
<p class="title">今日订单金额</p>
</header>
@ -50,7 +50,7 @@
<div class="header__item">
<header>
<div class="icon">
<img src="@/assets/icons/order.svg">
<svg-icon icon-class="order" width="16" height="16" color="#fff"></svg-icon>
</div>
<p class="title">今日活跃代理数</p>
</header>
@ -66,7 +66,7 @@
<div class="header__item">
<header>
<div class="icon">
<img src="@/assets/icons/order.svg">
<svg-icon icon-class="order" width="16" height="16" color="#fff"></svg-icon>
</div>
<p class="title">本月订单总数</p>
</header>
@ -85,6 +85,7 @@
</script>
<style scoped lang='scss'>
$colors: #00b882, #4d76eb, #db4c4d, #dc6333, #897dd7;
.header__item {
border-radius: 4px;
background: #fff;
@ -125,11 +126,6 @@ $colors: #00b882, #4d76eb, #db4c4d, #dc6333, #897dd7;
border-radius: 6px;
width: 25px;
height: 25px;
img {
width: 16px;
height: 16px;
}
}
}

View File

@ -1,13 +1,11 @@
<template>
<header>
<img src="@/assets/icons/mainproject.svg">
<p class="title">周订单统计</p>
</header>
<ProjectTitle title="周订单统计" />
<div class="line" ref="chartRef"></div>
</template>
<script setup lang='ts'>
import * as echarts from 'echarts';
import ProjectTitle from '@/components/ProjectTitle.vue';
const chartRef = ref<HTMLElement>();
@ -166,25 +164,6 @@ onMounted(() => {
</script>
<style scoped lang='scss'>
header {
height: 40px;
display: flex;
align-items: center;
padding: 0 16px;
border-bottom: 1px solid $dashLineColor;
gap:4px;
img {
width: 16px;
height: 16px;
}
.title {
color: $titleTextColor;
font-weight: bold;
}
}
.line {
height: 400px;
margin: 0 auto;

View File

@ -24,13 +24,14 @@
</div>
</n-tab-pane>
<n-tab-pane name="the beatles" tab="热门陪玩">
Hey Jude
<LsEmpty type="no-data" title="无数据" description="暂时没有陪玩登记"></LsEmpty>
</n-tab-pane>
</n-tabs>
</template>
<script setup lang='ts'>
import { CheckmarkCircle } from '@vicons/ionicons5'
import { CheckmarkCircle } from '@vicons/ionicons5';
import LsEmpty from '@/components/Ls-UI/LsEmpty.vue';
</script>
<style scoped lang='scss'>
.panel {
@ -44,6 +45,7 @@ import { CheckmarkCircle } from '@vicons/ionicons5'
height: 120px;
padding: 0 12px;
border-radius: 4px;
header {
display: flex;
justify-content: space-between;

View File

@ -1,8 +1,5 @@
<template>
<header>
<img src="@/assets/icons/mainproject.svg">
<p class="title">系统行为日志</p>
</header>
<ProjectTitle title="系统行为日志" />
<main>
<n-timeline>
<n-timeline-item type="success" title="login" content="用户罗澜登录了系统" time="2025-07-05 11:46" />
@ -12,27 +9,9 @@
</template>
<script setup lang='ts'>
import ProjectTitle from '@/components/ProjectTitle.vue';
</script>
<style scoped lang='scss'>
header {
height: 40px;
display: flex;
align-items: center;
padding: 0 16px;
border-bottom: 1px solid $dashLineColor;
gap: 4px;
img {
width: 16px;
height: 16px;
}
.title {
color: $titleTextColor;
font-weight: bold;
}
}
main {
padding:16px;
}

View File

@ -1,14 +1,12 @@
<template>
<header>
<img src="@/assets/icons/mainproject.svg">
<p class="title">近三月陪玩团情况</p>
</header>
<ProjectTitle title="近三月经营情况" />
<div class="bar" ref="chartRef"></div>
</template>
<script setup lang='ts'>
import { onMounted, ref } from 'vue';
import * as echarts from 'echarts';
import ProjectTitle from '@/components/ProjectTitle.vue';
const chartRef = ref<HTMLElement>();
@ -195,25 +193,6 @@ onMounted(() => {
</script>
<style scoped lang='scss'>
header {
height: 40px;
display: flex;
align-items: center;
padding: 0 16px;
border-bottom: 1px solid $dashLineColor;
gap: 4px;
img {
width: 16px;
height: 16px;
}
.title {
color: $titleTextColor;
font-weight: bold;
}
}
.bar {
height: 400px;
margin: 0 auto;

View File

@ -1,8 +1,5 @@
<template>
<header>
<img src="@/assets/icons/mainproject.svg">
<p class="title">快捷跳转</p>
</header>
<ProjectTitle title="快捷跳转" />
<main>
<div class="icon__item">
<div class="icon">
@ -14,27 +11,9 @@
</template>
<script setup lang='ts'>
import ProjectTitle from '@/components/ProjectTitle.vue';
</script>
<style scoped lang='scss'>
header {
height: 40px;
display: flex;
align-items: center;
padding: 0 16px;
border-bottom: 1px solid $dashLineColor;
gap: 4px;
img {
width: 16px;
height: 16px;
}
.title {
color: $titleTextColor;
font-weight: bold;
}
}
main {
min-height: 40px;
padding: 16px;

View File

@ -14,14 +14,7 @@
<script setup lang='ts'>
import {
LogOutOutline as HomeIcon,
LaptopOutline as WorkIcon,
CaretDownOutline,
HomeSharp,
PersonCircleSharp,
Layers,
Settings,
Menu
} from '@vicons/ionicons5'
import { MenuOption, NIcon } from 'naive-ui';
import { useAppStore } from '@/store/app';
@ -29,13 +22,14 @@ import { storeToRefs } from 'pinia';
import { RouterLink } from 'vue-router';
const appStore = useAppStore();
const activeKey = ref('default');
const { menuApp } = storeToRefs(appStore)
const { menuApp } = storeToRefs(appStore);
import SvgIcon from '@/components/SvgIcon.vue';
const expandIcon = () => {
return h(NIcon, null, { default: () => h(CaretDownOutline) })
}
function renderIcon(icon: Component) {
return () => h(NIcon, null, { default: () => h(icon) })
function renderIcon(iconClass: string) {
return () => h(SvgIcon, { iconClass, width: '16', height: '16' })
}
const menuOptions: MenuOption[] = [
{
@ -53,12 +47,12 @@ const menuOptions: MenuOption[] = [
{ default: () => '概览' }
),
key: 'default',
icon: renderIcon(HomeSharp)
icon: renderIcon('mainproject')
},
{
label: '角色管理',
key: 'role',
icon: renderIcon(PersonCircleSharp),
icon: renderIcon('mainproject'),
children: [
{
label: () => h(
@ -74,14 +68,14 @@ const menuOptions: MenuOption[] = [
{ default: () => '权限分配' }
),
key: 'role-auth',
icon: renderIcon(Layers)
icon: renderIcon('mainproject')
}
]
},
{
label: '系统配置',
key: 'system',
icon: renderIcon(Settings),
icon:renderIcon('mainproject'),
children: [
{
label: () => h(
@ -97,7 +91,7 @@ const menuOptions: MenuOption[] = [
{ default: () => '菜单管理' }
),
key: 'system',
icon: renderIcon(Menu)
icon: renderIcon('mainproject')
}
]
},
@ -112,7 +106,7 @@ const menuOptions: MenuOption[] = [
/* 添加平滑过渡效果 */
.n-layout-sider {
transition: width 0.3s var(--n-bezier);
transition: width 0.9s var(--n-bezier);
height: 100%;
}

View File

@ -2,12 +2,14 @@ import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import { fileURLToPath, URL } from "url";
import AutoImport from "unplugin-auto-import/vite";
import { createSvgIconsPlugin } from "vite-plugin-svg-icons";
import path from 'path';
// https://vite.dev/config/
export default defineConfig({
// ...其他配置
define: {
'process.env': process.env
"process.env": process.env,
},
plugins: [
vue(),
@ -16,22 +18,28 @@ export default defineConfig({
defaultExportByFilename: true,
dirs: ["./src/api"],
}),
createSvgIconsPlugin({
// 指定需要缓存的图标文件夹
iconDirs: [path.resolve(process.cwd(), "src/assets/icons")],
// 指定symbolId格式
symbolId: "icon-[name]",
}),
],
server: {
proxy: {
'/api':{
target:'http://localhost:3000/api',
"/api": {
target: "http://localhost:3000/api",
changeOrigin: true,
rewrite:(path) => path.replace(/^\api/,'')
}
rewrite: (path) => path.replace(/^\api/, ""),
},
},
},
css: {
preprocessorOptions: {
scss: {
additionalData: `@use "@/assets/styles/variable.scss" as *;` // 使用 @use 代替 @import
}
}
additionalData: `@use "@/assets/styles/variable.scss" as *;`, // 使用 @use 代替 @import
},
},
},
resolve: {
alias: {