Commit 95f71ab8 by HoMeTown

feat: init

parents
/** 请求环境配置 */
type ServiceEnv = Record<
EnvType,
{
// /** 请求地址 */
url?: string;
/** 代理地址 */
proxy: string;
}
>;
/** 环境配置 */
const serviceEnvConfig: ServiceEnv = {
dev: {
url: 'http://82.156.138.187:19001/sdream-api/manager/api', // 本地调试打开
proxy: '/api'
},
sit: {
url: 'http://82.156.138.187:19001/sdream-api/manager/api', // 本地调试打开
proxy: ''
},
uat: {
// url: 'https://uat-lfsplanning-platform.guominpension.com/manager/api', / 本地调试打开
proxy: '/api'
},
pre: {
// url: 'https://pre-lfsplanning-platform.guominpension.com/manager/api',/ 本地调试打开
proxy: '/api'
},
prod: {
url: 'http://82.156.138.187:19001/sdream-api/manager/api', // 本地调试打开
proxy: ''
}
};
/**
* 获取环境配置
* @param env 环境描述
*/
export function getEnvConfig(env: ImportMetaEnv) {
const { VITE_ENV_TYPE = 'dev' } = env;
const envConfig = {
http: serviceEnvConfig[VITE_ENV_TYPE]
};
return envConfig;
}
VITE_APP_NAME= guomin-pension-plan-admin
VITE_APP_TITLE= 晓得管理后台
VITE_APP_DESC= 晓得管理后台
# 权限路由模式: static | dynamic
VITE_AUTH_ROUTE_MODE=dynamic
VITE_HTTP_PROXY=true
VITE_VISUALIZER=false
VITE_APP_NAME= guomin-pension-plan-admin
VITE_APP_TITLE= 晓得管理后台
VITE_APP_DESC= 晓得管理后台
# 权限路由模式: static | dynamic
VITE_AUTH_ROUTE_MODE= dynamic
VITE_HTTP_PROXY= false
VITE_VISUALIZER= false
VITE_APP_NAME= guomin-pension-plan-admin
VITE_APP_TITLE= 晓得管理后台
VITE_APP_DESC= 晓得管理后台
# 权限路由模式: static | dynamic
VITE_AUTH_ROUTE_MODE=dynamic
VITE_HTTP_PROXY=false
VITE_VISUALIZER=false
VITE_APP_NAME= guomin-pension-plan-admin
VITE_APP_TITLE= 晓得管理后台
VITE_APP_DESC= 晓得管理后台
# 权限路由模式: static | dynamic
VITE_AUTH_ROUTE_MODE= dynamic
VITE_HTTP_PROXY= false
VITE_VISUALIZER= false
VITE_APP_NAME= guomin-pension-plan-admin
VITE_APP_TITLE= 晓得管理后台
VITE_APP_DESC= 晓得管理后台
# 权限路由模式: static | dynamic
VITE_AUTH_ROUTE_MODE= dynamic
VITE_HTTP_PROXY= false
VITE_VISUALIZER= false
*.sh
node_modules
lib
*.md
*.woff
*.ttf
.vscode
.idea
/dist/
/public
/docs
.vscode
.local
package.json
!.env-config.ts
components.d.ts
module.exports = {
env: {
browser: true,
es2021: true,
// 开启setup语法糖环境
'vue/setup-compiler-macros': true
},
globals: {
PROJECT_BUILD_TIME: 'readonly',
AMap: 'readonly',
BMap: 'readonly',
TMap: 'readonly'
},
parser: 'vue-eslint-parser',
parserOptions: {
ecmaVersion: 12,
parser: '@typescript-eslint/parser',
sourceType: 'module'
},
plugins: ['vue', '@typescript-eslint', 'prettier'],
extends: [
'airbnb-base',
'eslint:recommended',
'plugin:vue/vue3-recommended',
'plugin:prettier/recommended',
'@vue/eslint-config-typescript/recommended',
'@vue/eslint-config-prettier',
'@vue/typescript/recommended'
],
rules: {
'import/extensions': 'off',
'import/no-extraneous-dependencies': 'off',
'import/order': [
'error',
{
'newlines-between': 'never',
groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'],
pathGroups: [
{
pattern: 'vue',
group: 'external',
position: 'before'
},
{
pattern: 'vue-router',
group: 'external',
position: 'before'
},
{
pattern: 'vuex',
group: 'external',
position: 'before'
},
{
pattern: 'pinia',
group: 'external',
position: 'before'
},
// ui framework, such as "naive-ui"
// {
// pattern: 'naive-ui',
// group: 'external',
// position: 'before'
// },
{
pattern: '@/config',
group: 'internal',
position: 'before'
},
{
pattern: '@/settings',
group: 'internal',
position: 'before'
},
{
pattern: '@/enum',
group: 'internal',
position: 'before'
},
{
pattern: '@/plugins',
group: 'internal',
position: 'before'
},
{
pattern: '@/layouts',
group: 'internal',
position: 'before'
},
{
pattern: '@/views',
group: 'internal',
position: 'before'
},
{
pattern: '@/components',
group: 'internal',
position: 'before'
},
{
pattern: '@/router',
group: 'internal',
position: 'before'
},
{
pattern: '@/store',
group: 'internal',
position: 'before'
},
{
pattern: '@/composables',
group: 'internal',
position: 'before'
},
{
pattern: '@/hooks',
group: 'internal',
position: 'before'
},
{
pattern: '@/service',
group: 'internal',
position: 'before'
},
{
pattern: '@/utils',
group: 'internal',
position: 'before'
},
{
pattern: '@/assets',
group: 'internal',
position: 'before'
},
{
pattern: '@/**',
group: 'internal',
position: 'before'
},
{
pattern: '@/interface',
group: 'internal',
position: 'before'
}
],
pathGroupsExcludedImportTypes: [
'vue',
'vue-router',
'vuex',
'pinia'
// 'naive-ui'
]
}
],
'import/no-unresolved': 'off',
'import/prefer-default-export': 'off',
'max-classes-per-file': 'off',
'no-param-reassign': [
'error',
{
props: true,
ignorePropertyModificationsFor: ['state', 'acc', 'e']
}
],
'no-plusplus': 'off',
'no-shadow': 'off',
'no-unused-vars': 'off',
'no-use-before-define': 'off',
'vue/no-v-html': 'off',
'vue/multi-word-component-names': [
'error',
{
ignores: ['index']
}
],
'@typescript-eslint/ban-types': [
'error',
{
types: {
'{}': {
message: 'Use object instead',
fixWith: 'object'
}
}
}
],
'@typescript-eslint/no-empty-interface': [
'error',
{
allowSingleExtends: true
}
],
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-shadow': 'error',
'@typescript-eslint/no-unused-vars': ['warn', { ignoreRestSiblings: true, varsIgnorePattern: '^_' }],
'@typescript-eslint/no-use-before-define': ['error', { classes: true, functions: false, typedefs: false }]
},
overrides: [
{
files: ['*.vue'],
rules: {
'no-undef': 'off'
}
},
{
files: ['*.html'],
rules: {
'vue/comment-directive': 'off'
}
}
]
};
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
sdream-admin-fe
dist-ssr
coverage
*.local
stats.html
/cypress/videos/
/cypress/screenshots/
# Editor directories and files
.vscode/*
!.vscode/extensions.json
!.vscode/settings.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
deploy/privateKey.js
// .lintstagedrc.js
const files = ['*.js', '*.ts', '*.vue', '*.css', '*.scss', '*.less'];
const config = {};
files.forEach(file => {
config[file] = ['prettier --config .prettierrc.js --write', 'eslint --fix --ext .js'];
});
module.exports = config;
module.exports = { // https://prettier.io/docs/en/options.html
arrowParens: 'avoid',
bracketSameLine: false,
bracketSpacing: true,
embeddedLanguageFormatting: 'auto',
htmlWhitespaceSensitivity: 'css',
insertPragma: false,
jsxSingleQuote: false,
printWidth: 120,
proseWrap: 'preserve',
quoteProps: 'as-needed',
requirePragma: false,
semi: true,
singleQuote: true,
tabWidth: 2,
trailingComma: 'none',
useTabs: false,
vueIndentScriptAndStyle: false,
overrides: [
{
files: '*.html',
options: {
parser: 'html',
},
},
],
};
{
"editor.quickSuggestions": {
"strings": true
},
"workbench.iconTheme": "material-icon-theme",
"workbench.colorTheme": "Default Dark+",
"editor.tabSize": 2,
"editor.fontLigatures": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"editor.bracketPairColorization.enabled": true,
"editor.guides.bracketPairs": "active",
"git.enableSmartCommit": true,
"path-intellisense.mappings": {
"@": "${workspaceFolder}/src",
"~@": "${workspaceFolder}/src"
},
"gutterpreview.paths": {
"@": "/src",
"~@": "/src"
},
"terminal.integrated.cursorStyle": "line",
"files.associations": {
"*.env.*": "dotenv"
},
"[jsonc]": {
"editor.defaultFormatter": "vscode.json-language-features"
},
"[json]": {
"editor.defaultFormatter": "vscode.json-language-features"
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"javascript.updateImportsOnFileMove.enabled": "always",
"[javascriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"terminal.integrated.fontSize": 14,
"terminal.integrated.fontWeight": 500,
"[html]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[vue]": {
"editor.defaultFormatter": "Vue.volar"
},
"terminal.integrated.tabs.enabled": true,
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[markdown]": {
"editor.defaultFormatter": "yzhang.markdown-all-in-one"
},
"vue3snippets.enable-compile-vue-file-on-did-save-code": false,
"editor.formatOnSave": true,
"nuxt.isNuxtApp": false,
"typescript.tsdk": "node_modules/typescript/lib"
}
FROM ccr-2h88r0v7-pub.cnc.bj.baidubce.com/guomin-pension/basenginx:v1.0.0
# hide nginx version
RUN sed -i "/default_type/i\ server_tokens off;\n" /etc/nginx/nginx.conf
# static file
COPY output/ /admin-fe/
# nginx
COPY output/deploy/default.conf /etc/nginx/conf.d/
# start.sh
COPY output/deploy/start.sh /start.sh
RUN rm -rf /admin-fe/deploy
RUN chmod 755 /start.sh
CMD ["/start.sh"]
EXPOSE 8080
## 简介
guomin-pension-plan-admin-fe 是一个 企业级 的vue3项目,项目使用前端前沿技术栈搭建,代码规范极高!
## 技术栈
+ Vue3
+ Vite
+ TypeScript
+ Pinia
+ NaiveUI
+ VxeTable
+ Windicss
+ Eslint
+ Prettier
+ ....
## 安装使用
- 安装依赖
```bash
npm i
```
- 运行
```bash
npm run dev
```
- 打包
```bash
npm run build
```
import type { BuildOptions } from 'vite';
/** vite打包 */
export function createViteBuild(viteEnv: ImportMetaEnv, viteCommand: string) {
const isProd = viteEnv.VITE_ENV_TYPE === 'prod';
const isBuild = viteCommand === 'build';
const build: Record<string, any | BuildOptions> = {
outDir: 'sdream-admin-fe',
brotliSize: false,
sourcemap: false,
chunkSizeWarningLimit: 500,
minify: 'terser', // 默认 esbuild
terserOptions: {
compress: {
drop_console: isBuild && isProd,
drop_debugger: isBuild && isProd
}
},
rollupOptions: {
output: {
manualChunks(id: string) {
if (id.includes('node_modules')) {
return id.toString().split('node_modules/')[1].split('/')[0].toString();
}
return null;
},
chunkFileNames: (chunkInfo: Record<string, any>) => {
const facadeModuleId = chunkInfo.facadeModuleId ? chunkInfo.facadeModuleId.split('/') : [];
const fileName = facadeModuleId[facadeModuleId.length - 2] || '[name]';
return `js/${fileName}/[name].[hash].js`;
}
}
}
};
return build;
}
import dayjs from 'dayjs';
/** 项目构建时间 */
const PROJECT_BUILD_TIME = JSON.stringify(dayjs().format('YYYY-MM-DD HH:mm:ss'));
export const viteDefine = {
PROJECT_BUILD_TIME
};
export * from './path';
export * from './define';
export * from './proxy';
export * from './build';
import { fileURLToPath } from 'url';
/**
* 解析路径
* @param basePath - 基础路径
*/
export function resolvePath(rootPath: string, basePath: string) {
const root = fileURLToPath(new URL(rootPath, basePath));
const src = `${root}src`;
return {
root,
src
};
}
// 设置代理
import type { ProxyOptions } from 'vite';
import { getEnvConfig } from '../../.env-config';
/**
* 设置网络代理
* @param viteEnv - vite环境描述
*/
export function createViteProxy(viteEnv: ImportMetaEnv) {
const isOpenProxy = viteEnv.VITE_HTTP_PROXY === 'true';
if (!isOpenProxy) return undefined;
const { http } = getEnvConfig(viteEnv);
const proxy: Record<string, string | ProxyOptions> = {
[http.proxy]: {
target: http.url,
changeOrigin: true,
rewrite: path => path.replace(new RegExp(`^${http.proxy}`), '')
}
};
return proxy;
}
export * from './plugins';
export * from './config';
// 自动引入插件
import Icons from 'unplugin-icons/vite';
import IconsResolver from 'unplugin-icons/resolver';
import Components from 'unplugin-vue-components/vite';
import { FileSystemIconLoader } from 'unplugin-icons/loaders';
export default (srcPath: string) => {
return [
Icons({
compiler: 'vue3',
customCollections: {
custom: FileSystemIconLoader(`${srcPath}/assets/svg`)
},
scale: 1,
defaultClass: 'inline-block'
}),
Components({
dts: true,
resolvers: [IconsResolver({ customCollections: ['custom'], componentPrefix: 'icon' })]
})
];
};
// html插件(注入变量,压缩代码等)
import { loadEnv } from 'vite';
import type { ConfigEnv, PluginOption } from 'vite';
import { createHtmlPlugin } from 'vite-plugin-html';
export default (configEnv: ConfigEnv): PluginOption[] => {
const viteEnv = loadEnv(configEnv.mode, process.cwd()) as ImportMetaEnv;
return createHtmlPlugin({
minify: true,
inject: {
data: {
appName: viteEnv.VITE_APP_NAME,
appTitle: viteEnv.VITE_APP_TITLE
}
}
});
};
import type { ConfigEnv, PluginOption } from 'vite';
import vue from './vue';
import html from './html';
import autoImport from './auto-import';
import windicss from './windicss';
import mock from './mock';
import visualizer from './visualizer';
import styleImport from './style-import';
/**
* vite插件
* @param configEnv - 环境
* @param srcPath - src路径
* @param viteEnv - 环境变量配置
*/
export function setupVitePlugins(
configEnv: ConfigEnv,
srcPath: string,
viteEnv: ImportMetaEnv
): (PluginOption | PluginOption[])[] {
const plugins: any[] = [vue, html(configEnv), ...autoImport(srcPath), windicss, mock, styleImport];
if (configEnv.command === 'build' && viteEnv.VITE_VISUALIZER === 'true') {
plugins.push(visualizer as any);
}
return plugins;
}
import vueJsx from '@vitejs/plugin-vue-jsx'; // 要安装@vitejs/plugin-vue-jsx
// export default vueJsx({});
// mock插件
import { viteMockServe } from 'vite-plugin-mock';
export default viteMockServe({
mockPath: 'mock',
injectCode: `
import { setupMockServer } from '../mock';
setupMockServer();
`
});
import { createStyleImportPlugin, VxeTableResolve } from 'vite-plugin-style-import';
export default createStyleImportPlugin({
resolves: [VxeTableResolve()]
});
// 构建的依赖大小占比分析插件
import { visualizer } from 'rollup-plugin-visualizer';
export default visualizer({
gzipSize: true,
open: true,
brotliSize: true
});
// vue相关vite插件
import vue from '@vitejs/plugin-vue';
export default vue({});
// css框架插件
import windiCSS from 'vite-plugin-windicss';
export default windiCSS();
module.exports = { extends: ['@commitlint/config-conventional'] };
// generated by unplugin-vue-components
// We suggest you to commit this file into source control
// Read more: https://github.com/vuejs/vue-next/pull/3399
declare module 'vue' {
export interface GlobalComponents {
BarChart: typeof import('./src/components/custom/BarChart.vue')['default']
BetterScroll: typeof import('./src/components/custom/BetterScroll.vue')['default']
CountTo: typeof import('./src/components/custom/CountTo.vue')['default']
DarkModeContainer: typeof import('./src/components/common/DarkModeContainer.vue')['default']
DarkModeSwitch: typeof import('./src/components/common/DarkModeSwitch.vue')['default']
HoverContainer: typeof import('./src/components/common/HoverContainer.vue')['default']
IconAntDesignCloseOutlined: typeof import('~icons/ant-design/close-outlined')['default']
IconAntDesignEnterOutlined: typeof import('~icons/ant-design/enter-outlined')['default']
IconAntDesignSettingOutlined: typeof import('~icons/ant-design/setting-outlined')['default']
IconCustomAvatar: typeof import('~icons/custom/avatar')['default']
IconGridiconsFullscreen: typeof import('~icons/gridicons/fullscreen')['default']
IconGridiconsFullscreenExit: typeof import('~icons/gridicons/fullscreen-exit')['default']
IconIcOutlineCheck: typeof import('~icons/ic/outline-check')['default']
IconLineMdMenuFoldLeft: typeof import('~icons/line-md/menu-fold-left')['default']
IconLineMdMenuUnfoldLeft: typeof import('~icons/line-md/menu-unfold-left')['default']
IconMdiArrowDownThin: typeof import('~icons/mdi/arrow-down-thin')['default']
IconMdiArrowUpThin: typeof import('~icons/mdi/arrow-up-thin')['default']
IconMdiClose: typeof import('~icons/mdi/close')['default']
IconMdiMoonWaningCrescent: typeof import('~icons/mdi/moon-waning-crescent')['default']
IconMdiPin: typeof import('~icons/mdi/pin')['default']
IconMdiPinOff: typeof import('~icons/mdi/pin-off')['default']
IconMdiRefresh: typeof import('~icons/mdi/refresh')['default']
IconMdiWhiteBalanceSunny: typeof import('~icons/mdi/white-balance-sunny')['default']
IconPhCaretDoubleLeftBold: typeof import('~icons/ph/caret-double-left-bold')['default']
IconPhCaretDoubleRightBold: typeof import('~icons/ph/caret-double-right-bold')['default']
IconSelect: typeof import('./src/components/custom/IconSelect.vue')['default']
IconUilSearch: typeof import('~icons/uil/search')['default']
LineChart: typeof import('./src/components/custom/LineChart.vue')['default']
NaiveProvider: typeof import('./src/components/common/NaiveProvider.vue')['default']
StatusTag: typeof import('./src/components/business/StatusTag.vue')['default']
SystemLogo: typeof import('./src/components/common/SystemLogo.vue')['default']
TopFlagTag: typeof import('./src/components/business/TopFlagTag.vue')['default']
UploadImage: typeof import('./src/components/business/UploadImage.vue')['default']
WebSiteLink: typeof import('./src/components/custom/WebSiteLink.vue')['default']
}
}
export { }
#本配置文件已修改,需要测试是否正确
server {
listen 8080;
server_name localhost;
add_header Strict-Transport-Security "max-age=31536000; includeSubdomains; preload";
# add_header Content-Security-Policy "default-src 'self' localhost:8080 'unsafe-inline' 'unsafe-eval' blob: data:;";
add_header X-Frame-Options SAMEORIGIN;
location / {
root /admin-fe;
# 配置页面不缓存html和htm结尾的文件
if ($request_filename ~* .*\.(?:htm|html)$) {
add_header Cache-Control "private, no-store, no-cache, must-revalidate, proxy-revalidate";
}
index index.html;
try_files $uri $uri/ /index.html;
add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection 1;
}
#静态资源
location /static {
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Autthorization';
add_header Strict-Transport-Security "max-age=31536000; includeSubdomains; preload";
add_header X-Frame-Options SAMEORIGIN;
if ($request_method = 'OPTIONS') {
return 204;
}
alias /admin-fe/static/;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
#!/bin/sh
exec nginx -g 'daemon off;'
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<link rel="stylesheet" href="/resource/loading.css" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title><%= appTitle %></title>
<script src="https://rescdn.qqmail.com/node/ww/wwopenmng/js/sso/wwLogin-1.0.0.js" type="text/javascript"></script>
</head>
<body>
<div id="app">
<div class="loading-container">
<div class="loading-spin__container">
<img class="loading-logo-img" src="./src/assets/images/logo-2-removebg-preview.png" alt="" />
<div class="loading-spin">
<div class="left-0 top-0 loading-spin-item"></div>
<div class="left-0 bottom-0 loading-spin-item loading-delay-500"></div>
<div class="right-0 top-0 loading-spin-item loading-delay-1000"></div>
<div class="right-0 bottom-0 loading-spin-item loading-delay-1500"></div>
</div>
</div>
<!-- <h2 class="loading-title"><%= appTitle %></h2> -->
</div>
<script src="/resource/loading.js"></script>
</div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
import type { MockMethod } from 'vite-plugin-mock';
const token: ApiAuth.Token = {
token: ''
};
const apis: MockMethod[] = [
// 密码登录
{
url: '/mock/loginByPwd',
method: 'post',
response: (): Service.MockServiceResult<ApiAuth.Token> => {
return {
code: 200,
message: 'ok',
data: token
};
}
},
// 验证码登录
{
url: '/mock/loginByCode',
method: 'post',
response: (): Service.MockServiceResult<ApiAuth.Token> => {
return {
code: 200,
message: 'ok',
data: token
};
}
},
// // 获取用户信息(请求头携带token)
// {
// url: '/mock/getUserInfo',
// method: 'get',
// response: (): Service.MockServiceResult<ApiAuth.UserInfo> => {
// return {
// code: 200,
// message: 'ok',
// data: {
// userId: -1,
// userName: 'Soybean',
// userPhone: '15170283876',
// userRole: 'super'
// }
// };
// }
// },
{
url: '/mock/testToken',
method: 'post',
response: (option: any): Service.MockServiceResult<true | null> => {
if (option.headers['X-Token'] !== token.token) {
return {
code: 66666,
message: 'token 失效',
data: null
};
}
return {
code: 200,
message: 'ok',
data: true
};
}
},
{
url: '/mock/updateToken',
method: 'post',
response: (): Service.MockServiceResult<ApiAuth.Token> => {
return {
code: 200,
message: 'ok',
data: token
};
}
}
];
export default apis;
import type { MockMethod } from 'vite-plugin-mock';
const apis: MockMethod[] = [
{
url: '/mock/busi/budget/order/page',
method: 'get',
response: (): Service.MockServiceResult => {
return {
code: 200,
message: 'ok',
records: [
{
budgetCode: 'YTY8899912',
orderId: 1,
budgetType: '00',
contractUserName: '小王',
contractDeptName: '部门一',
amount: '12093',
submitDate: '2022-04-18',
updateTime: '2023-03-03',
budgetStatus: '00',
customer: '小李'
},
{
budgetCode: 'YTY8899912',
orderId: 1,
budgetType: '00',
contractUserName: '小李',
contractDeptName: '部门一',
amount: '12093',
submitDate: '2022-04-18',
updateTime: '2023-03-03',
budgetStatus: '00',
customer: '小李'
}
]
};
}
}
];
export default apis;
import auth from './auth';
import route from './route';
import budgetRoder from './budget-order';
export default [...auth, ...route, ...budgetRoder];
import type { MockMethod } from 'vite-plugin-mock';
const routes: AuthRoute.Route[] = [
{
name: 'home',
path: '/home',
component: 'self',
meta: {
title: '首页',
requiresAuth: true,
singleLayout: 'basic',
icon: 'tabler:home-2',
order: 0
}
},
{
name: 'exception',
path: '/exception',
component: 'basic',
children: [
{
name: 'exception_403',
path: '/exception/403',
component: 'self',
meta: {
title: '异常页403',
requiresAuth: true,
icon: 'ic:baseline-block'
}
},
{
name: 'exception_404',
path: '/exception/404',
component: 'self',
meta: {
title: '异常页404',
requiresAuth: true,
icon: 'ic:baseline-web-asset-off'
}
},
{
name: 'exception_500',
path: '/exception/500',
component: 'self',
meta: {
title: '异常页500',
requiresAuth: true,
icon: 'ic:baseline-wifi-off'
}
}
],
meta: {
title: '异常页',
icon: 'ant-design:exception-outlined',
order: 5
}
}
];
function dataMiddleware(data: AuthRoute.Route[]): AuthRoute.Route[] {
// const routeHomeName: AuthRoute.RouteKey = 'home';
function sortRoutes(sorts: AuthRoute.Route[]) {
return sorts.sort((next, pre) => Number(next.meta?.order) - Number(pre.meta?.order));
}
return sortRoutes(data);
}
const apis: MockMethod[] = [
{
url: '/mock/getRouters',
method: 'get',
response: (): Service.MockServiceResult => {
return {
code: 200,
message: 'ok',
data: dataMiddleware(routes)
};
}
}
];
export default apis;
import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer';
import api from './api';
export function setupMockServer() {
createProdMockServer(api);
}
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "guomin-pension-plan-admin-fe",
"version": "0.9.3",
"scripts": {
"dev": "cross-env VITE_ENV_TYPE=dev vite",
"dev:sit": "cross-env VITE_ENV_TYPE=sit vite",
"dev:prod": "cross-env VITE_ENV_TYPE=prod vite",
"build": "cross-env VITE_ENV_TYPE=prod vite build --mode=production",
"build:sit": "cross-env VITE_ENV_TYPE=sit vite build --mode=sit",
"preview": "vite preview --port 5050",
"typecheck": "vue-tsc --noEmit",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix",
"format": "prettier --write ./**/*.{vue,ts,tsx,js,jsx,css,less,scss,json,md}",
"commit": "git cz"
},
"config": {
"commitizen": {
"path": "./node_modules/cz-conventional-changelog"
}
},
"dependencies": {
"@better-scroll/core": "^2.4.2",
"@soybeanjs/vue-materials": "^0.2.0",
"@vueuse/core": "^8.2.3",
"axios": "^0.26.1",
"clipboard": "^2.0.10",
"clsx": "^2.1.1",
"colord": "^2.9.2",
"commitizen": "^4.3.0",
"crypto-js": "^4.1.1",
"dayjs": "^1.11.0",
"echarts": "^5.4.0",
"form-data": "^4.0.0",
"framer-motion": "^11.3.2",
"lodash-es": "^4.17.21",
"mini-svg-data-uri": "^1.4.4",
"naive-ui": "^2.28.2",
"pinia": "^2.0.13",
"print-js": "^1.6.0",
"qs": "^6.10.3",
"request": "^2.88.2",
"tailwind-merge": "^2.4.0",
"ua-parser-js": "^1.0.2",
"vue": "^3.2.31",
"vue-router": "^4.0.14",
"vxe-table": "4.3.5",
"xe-utils": "3.5.7"
},
"devDependencies": {
"@amap/amap-jsapi-types": "^0.0.8",
"@commitlint/cli": "^16.2.3",
"@commitlint/config-conventional": "^16.2.1",
"@iconify/json": "^2.1.23",
"@iconify/vue": "^3.2.1",
"@types/bmapgl": "^0.0.5",
"@types/crypto-js": "^4.1.1",
"@types/node": "^17.0.23",
"@types/qs": "^6.9.7",
"@types/ua-parser-js": "^0.7.36",
"@typescript-eslint/eslint-plugin": "^5.17.0",
"@typescript-eslint/parser": "^5.17.0",
"@vicons/ionicons4": "^0.12.0",
"@vicons/ionicons5": "^0.12.0",
"@vitejs/plugin-vue": "^2.3.1",
"@vitejs/plugin-vue-jsx": "^1.3.10",
"@vue/eslint-config-prettier": "^7.0.0",
"@vue/eslint-config-typescript": "^10.0.0",
"@vue/tsconfig": "^0.1.3",
"autoprefixer": "^10.4.19",
"cross-env": "^7.0.3",
"cz-conventional-changelog": "^3.3.0",
"cz-customizable": "^6.3.0",
"eslint": "^7.32.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-import": "^2.25.4",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-vue": "^8.5.0",
"husky": "^7.0.4",
"increase-memory-limit": "^1.0.7",
"lint-staged": "^12.3.7",
"mockjs": "^1.1.0",
"patch-package": "^6.4.7",
"postcss": "^8.4.39",
"postinstall-postinstall": "^2.1.0",
"prettier": "^2.6.2",
"rollup-plugin-visualizer": "^5.6.0",
"sass": "^1.49.11",
"tailwindcss": "^3.4.4",
"tailwindcss-dotted-background": "^1.1.0",
"typescript": "^4.8.2",
"unplugin-icons": "^0.14.1",
"unplugin-vue-components": "^0.18.5",
"vite": "^2.9.1",
"vite-plugin-html": "^3.2.0",
"vite-plugin-html-template": "^1.1.2",
"vite-plugin-mock": "^2.9.6",
"vite-plugin-style-import": "^2.0.0",
"vite-plugin-windicss": "^1.8.3",
"vue-tsc": "^1.0.9",
"vueuc": "^0.4.28",
"windicss": "^3.5.1"
},
"description": "GuangHua Admin 是一个基于 Vue3、Vite、TypeScript、Naive UI 的后台模板,它使用了最新的前端技术栈,内置丰富的主题配置,有着极高的代码规范!",
"main": ".cz-config.js",
"keywords": [],
"author": "",
"license": "ISC"
}
// postcss.config.js
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
.loading-container {
position: fixed;
left: 0;
top: 0;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
background-color: #f6f7f9;
}
.loading-spin__container {
width: 76px;
height: 76px;
position: relative;
}
.loading-logo-img {
position: absolute;
width: 40px;
left: 50%;
margin-left: -20px;
top: 50%;
margin-top: -20px;
}
.loading-spin {
position: relative;
height: 100%;
animation: loadingSpin 1s linear infinite;
}
.left-0 {
left: 0;
}
.right-0 {
right: 0;
}
.top-0 {
top: 0;
}
.bottom-0 {
bottom: 0;
}
.loading-spin-item {
position: absolute;
height: 10px;
width: 10px;
background-color: #000;
border-radius: 8px;
-webkit-animation: loadingPulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
animation: loadingPulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
@keyframes bounce {
0% {
transform: scale(1);
}
50% {
transform: scale(1.1);
}
100% {
transform: scale(1);
}
}
@keyframes loadingSpin {
from {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
to {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@keyframes loadingPulse {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}
.loading-delay-500 {
-webkit-animation-delay: 500ms;
animation-delay: 500ms;
}
.loading-delay-1000 {
-webkit-animation-delay: 1000ms;
animation-delay: 1000ms;
}
.loading-delay-1500 {
-webkit-animation-delay: 1500ms;
animation-delay: 1500ms;
}
.loading-title {
font-size: 28px;
font-weight: 500;
color: #646464;
}
function addThemeColorCssVars() {
const key = '__THEME_COLOR__';
const themeColor = '#c62a22';
// const themeColor = window.localStorage.getItem(key) || '#E6000A';
const cssVars = `--primary-color: ${themeColor}`;
document.documentElement.style.cssText = cssVars;
}
addThemeColorCssVars();
<template>
<n-config-provider
:theme="theme.naiveTheme"
:theme-overrides="theme.naiveThemeOverrides"
:locale="zhCN"
:date-locale="dateZhCN"
class="h-full"
>
<naive-provider>
{{ theme.naiveTheme }}
<router-view />
</naive-provider>
</n-config-provider>
</template>
<script setup lang="ts">
import { zhCN, dateZhCN } from 'naive-ui';
import { useThemeStore, subscribeStore } from '@/store';
const theme = useThemeStore();
subscribeStore();
</script>
<style scoped></style>
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" class="feather feather-activity"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" class="feather feather-at-sign"><circle cx="12" cy="12" r="4"></circle><path d="M16 8v5a3 3 0 0 0 6 0v-1a10 10 0 1 0-3.92 7.94"></path></svg>
\ No newline at end of file
<svg id="eb3b8723-41b0-4d9f-a59f-ffd4b630357a" data-name="图层 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 134.49 134.49"><path d="M134.49,67.25A67.25,67.25,0,1,1,67.25,0a67.23,67.23,0,0,1,67.24,67.25" style="fill:#f3f3f2"/><polygon points="64.47 103.16 64.47 103.16 64.47 103.16 64.47 103.16" style="fill:#f1caae"/><path d="M66.87,90.15c8.11-6.21,11-10.25,12.24-11a17.94,17.94,0,0,1-.69-4.23c-4,3.34-5.73,4.49-11,4.31-5.69.19-7.28-1.15-12-5.14L55.35,74a19,19,0,0,1-.72,5.14C55.86,79.89,58.76,83.93,66.87,90.15Z" style="fill:#f1caae"/><path d="M61.56,121.66c.31-2,.61-4,.9-5.91h0l0-.11c.93-6,1.7-10.86,2-12.48h0a1.17,1.17,0,0,1-.36-.34h0a.6.6,0,0,1-.09-.13h0c-.16-.36-.32-.69-.48-1,0-.06-.07-.13-.1-.2a16.73,16.73,0,0,1-2-4.43c0-.15,0-.3-.07-.46v0a4,4,0,0,0-1,2.65c.33,3.56-10.83-9.56-10.65-14.6a38.55,38.55,0,0,1-4,1.9h0A219.52,219.52,0,0,0,61.56,121.66Z" style="fill:#9d9d9d"/><path d="M23.63,94.06h0l-.09,0h.05S23.62,94.07,23.63,94.06Z" style="fill:#9d9d9d"/><path d="M73.3,99.2a4,4,0,0,0-1-2.64.06.06,0,0,1,0,0c0,.16,0,.31-.06.46A7.17,7.17,0,0,1,72,98h0a27.91,27.91,0,0,1-1.71,3.52l-.11.21c-.16.31-.32.65-.48,1a1,1,0,0,1-.09.13h0a1.1,1.1,0,0,1-.37.34c.29,1.6,1.06,6.35,2,12.32a2.34,2.34,0,0,0,0,.26h0c.23,1.54.48,3.15.73,4.81A208.85,208.85,0,0,0,87.47,86.38h0A37.69,37.69,0,0,1,84,84.7C84,89.82,73,102.73,73.3,99.2Z" style="fill:#9d9d9d"/><path d="M109.7,94.11a5.22,5.22,0,0,1,.63.11l-.73-.28h0S109.68,94,109.7,94.11Z" style="fill:#9d9d9d"/><path d="M23.6,94.09h-.05a4.67,4.67,0,0,0-.84.15c-3,.81-5.08,8.11-6.94,16.26a67.51,67.51,0,0,0,16.1,13.93,33.36,33.36,0,0,0-.42-4.63C30.32,113.55,22.66,94.93,23.6,94.09Z" style="fill:#959494"/><path d="M117.63,111.76c-2-8.62-4-16.67-7.21-17.52l-.09,0a5.22,5.22,0,0,0-.63-.11c.39,1.91-6.82,19.65-7.91,25.69a34.76,34.76,0,0,0-.44,5.4" style="fill:#959494"/><path d="M71.29,119.06H69.64v-3.73h1.58l0,.15c-.92-6-1.69-10.72-2-12.32l-.1.07a1.29,1.29,0,0,1-.28.08v.09H68l-.89.06v0h-.56v0l-.88-.06h-.86v-.09a1.2,1.2,0,0,1-.27-.08l-.11-.07h0c-.29,1.62-1.06,6.44-2,12.48l0-.31h1.59v3.73H62.46v-3.31c-.29,1.87-.59,3.86-.9,5.91h0c0-.34.1-.69.15-1v1.28l.75,1.25v-2.62h1.65v3.73h-1c.57.91,1.14,1.78,1.7,2.58v-1.09H66.5v2.82c.25-.29.5-.6.75-.91v-1.91h1.42c.34-.48.68-1,1-1.49h0v-3.73h1.65v1.22l.73-1.22c-.25-1.66-.5-3.27-.73-4.81Zm-1.65-13.74c.15.91.33,2,.53,3.3h-.53Zm0,4.79h.77c.18,1.16.38,2.4.58,3.73H69.64Zm-3.14-5.22v3.73H64.85v-3.73Zm-2.39,9H62.75c.21-1.33.4-2.57.59-3.73h.77Zm0-5.22h-.54l.54-3.31Zm2.39,15.66H64.85v-3.73H66.5Zm0-5.22H64.85v-3.73H66.5Zm0-5.22H64.85v-3.73H66.5Zm2.39,10.44H67.25v-3.73h1.64Zm0-5.22H67.25v-3.73h1.64Zm0-5.22H67.25v-3.73h1.64Zm0-5.22H67.25v-3.73h1.64Z" style="fill:#f0f0f0"/><path d="M63.53,101.69c.16.32.32.65.48,1h0C63.85,102.34,63.69,102,63.53,101.69Z" style="fill:#fff"/><path d="M61.41,96.55v0Z" style="fill:#fff"/><path d="M72.27,97.06A7.17,7.17,0,0,1,72,98,7.17,7.17,0,0,0,72.27,97.06Z" style="fill:#fff"/><path d="M72.34,96.56h0a.09.09,0,0,1,0,0A.06.06,0,0,0,72.34,96.56Z" style="fill:#fff"/><path d="M70.21,101.7c-.16.32-.32.64-.48,1C69.89,102.35,70.05,102,70.21,101.7Z" style="fill:#fff"/><path d="M64.58,103.23a1.2,1.2,0,0,0,.27.08V99.67H66.5v3.73h-.79l.88.06v0h.56v0l.89-.06h-.79V99.67h1.64v3.64a1.29,1.29,0,0,0,.28-.08l.1-.07a1.1,1.1,0,0,0,.37-.34V99.67h1.64c-.29.57-.62,1.16-1,1.82A27.91,27.91,0,0,0,72,98V96.15c-.23-.29-.48-.57-.74-.86v2.89H69.64V94.45h.82c-.55-.52-1.11-1-1.63-1.49H67.25v-1.5a3.56,3.56,0,0,1-.38-.47,2.79,2.79,0,0,1-.37.47V93H64.91c-.52.47-1.08,1-1.62,1.49h.82v3.73H62.46V95.29c-.27.28-.52.57-.75.86V98a7.13,7.13,0,0,1-.23-.91,16.73,16.73,0,0,0,2,4.43c-.34-.65-.67-1.25-1-1.82h1.64v3.15a1.17,1.17,0,0,0,.36.34h0Zm2.67-8.78h1.64v3.73H67.25Zm-2.4,0H66.5v3.73H64.85Z" style="fill:#fff"/><polygon points="66.5 99.67 64.85 99.67 64.85 103.31 64.85 103.4 65.71 103.4 66.5 103.4 66.5 99.67" style="fill:#040000"/><rect x="64.85" y="104.89" width="1.65" height="3.73" style="fill:#040000"/><rect x="64.85" y="94.45" width="1.65" height="3.73" style="fill:#040000"/><rect x="64.85" y="110.11" width="1.65" height="3.73" style="fill:#040000"/><path d="M64.85,125.77v1.09c.49.7,1,1.35,1.45,2l.2-.23v-2.82Z" style="fill:#040000"/><rect x="64.85" y="120.55" width="1.65" height="3.73" style="fill:#040000"/><rect x="64.85" y="115.33" width="1.65" height="3.73" style="fill:#040000"/><path d="M61.56,121.66l.15.26v-1.28C61.66,121,61.61,121.32,61.56,121.66Z" style="fill:#040000"/><path d="M62.46,98.18h1.65V94.45h-.82c-.29.28-.56.56-.83.84Z" style="fill:#040000"/><path d="M61.71,96.15l-.3.4v0c0,.16,0,.31.07.46a7.13,7.13,0,0,0,.23.91Z" style="fill:#040000"/><path d="M64.11,99.67H62.47c.29.57.62,1.17,1,1.82,0,.07.07.14.1.2.16.32.32.65.48,1h0a.6.6,0,0,0,.09.13h0Z" style="fill:#040000"/><path d="M64.11,120.55H62.46v2.62q.34.57.69,1.11h1Z" style="fill:#040000"/><path d="M64.11,119.06v-3.73H62.52l0,.31,0,.11h0v3.31Z" style="fill:#040000"/><path d="M64.11,108.62v-3.31l-.54,3.31Z" style="fill:#040000"/><path d="M62.75,113.84h1.36v-3.73h-.77C63.15,111.27,63,112.51,62.75,113.84Z" style="fill:#040000"/><path d="M69.64,120.55v3.73h0c.53-.8,1.06-1.63,1.6-2.51v-1.22Z" style="fill:#040000"/><path d="M69.64,115.33v3.73h1.65v-3.32h0a2.34,2.34,0,0,1,0-.26l0-.15Z" style="fill:#040000"/><path d="M66.5,91.46c-.4.43-1,.94-1.59,1.5H66.5Z" style="fill:#040000"/><path d="M68.83,93c-.63-.56-1.19-1.07-1.58-1.5V93Z" style="fill:#040000"/><rect x="67.25" y="110.11" width="1.65" height="3.73" style="fill:#040000"/><rect x="67.25" y="120.55" width="1.65" height="3.73" style="fill:#040000"/><rect x="67.25" y="115.33" width="1.65" height="3.73" style="fill:#040000"/><polygon points="68.89 99.67 67.25 99.67 67.25 103.4 68.04 103.4 68.89 103.4 68.89 103.31 68.89 99.67" style="fill:#040000"/><rect x="67.25" y="104.89" width="1.65" height="3.73" style="fill:#040000"/><path d="M67.25,127.68c.47-.6.94-1.23,1.42-1.91H67.25Z" style="fill:#040000"/><rect x="67.25" y="94.45" width="1.65" height="3.73" style="fill:#040000"/><path d="M69.64,105.32v3.3h.53C70,107.35,69.79,106.23,69.64,105.32Z" style="fill:#040000"/><path d="M69.64,99.67v3.15h0a1,1,0,0,0,.09-.13c.16-.36.32-.68.48-1l.11-.21c.34-.66.67-1.25,1-1.82Z" style="fill:#040000"/><path d="M72,98h0a7.17,7.17,0,0,0,.24-.91c0-.15,0-.3.06-.46a.09.09,0,0,0,0,0c-.1-.13-.2-.27-.31-.4V98Z" style="fill:#040000"/><path d="M69.64,98.18h1.65V95.29l-.83-.84h-.82Z" style="fill:#040000"/><path d="M70.41,110.11h-.77v3.73H71C70.79,112.51,70.59,111.27,70.41,110.11Z" style="fill:#040000"/><path d="M60.45,99.2a4,4,0,0,1,1-2.65h0l.3-.4c.23-.29.48-.58.75-.86s.54-.56.83-.84c.54-.52,1.1-1,1.62-1.49s1.19-1.07,1.59-1.5a2.79,2.79,0,0,0,.37-.47,3.56,3.56,0,0,0,.38.47c.39.43,1,.94,1.58,1.5s1.08,1,1.63,1.49l.83.84c.26.29.51.57.74.86.11.13.21.27.31.4h0a4,4,0,0,1,1,2.64C73,102.73,84,89.82,84,84.7c0-.12,0-.24,0-.36-.09-.72-2.2-1.82-4.15-5.11-.15-.26-.36-.27-.68-.08-1.23.75-4.13,4.79-12.24,11-8.11-6.22-11-10.26-12.24-11-.31-.19-.53-.18-.68.08-2,3.29-4.05,4.39-4.14,5.11a2.17,2.17,0,0,0,0,.26C49.62,89.64,60.78,102.76,60.45,99.2Z" style="fill:#838384"/><path d="M62.49,132.11a13.73,13.73,0,0,0,3.81-3.29c-.47-.61-1-1.26-1.45-2s-1.13-1.67-1.7-2.58q-.34-.54-.69-1.11l-.75-1.25-.15-.26h0A219.52,219.52,0,0,1,45.81,86.5h0c-.49-1.25-21,7-22.17,7.56h0s0,0,0,0c-.94.84,6.72,19.46,7.85,25.71a33.36,33.36,0,0,1,.42,4.63A66.72,66.72,0,0,0,62,134.27C62.15,133,62.3,132.21,62.49,132.11Z" style="fill:#282629"/><path d="M101.35,125.2a34.76,34.76,0,0,1,.44-5.4c1.09-6,8.3-23.78,7.91-25.69,0-.08,0-.15-.09-.17h0c-1.17-.52-21.69-8.84-22.13-7.56h0A208.85,208.85,0,0,1,72,120.55l-.73,1.22c-.54.88-1.07,1.71-1.6,2.51-.34.51-.68,1-1,1.49-.48.68-1,1.31-1.42,1.91-.25.31-.5.62-.75.91l-.2.23a13.73,13.73,0,0,1-3.81,3.29c-.19.1-.34.89-.47,2.16,1.73.13,3.47.22,5.23.22a66.88,66.88,0,0,0,34.1-9.29" style="fill:#1a1a1b"/><path d="M67.41,79.23c5.28.18,7-1,11-4.31l1-.83c4.93-4.19,2.44-12.94,5.44-12.39s6.29-7.44,4.18-9.45c-1-.09-1.87.88-2.66,2.2-.89,1.49-.54,3.79-1.42,3.74-1.19-4-.93-7.62-.69-11.81C76,48.8,67.47,50.06,60.59,45c-1.41-1-2.77-2.4-4.91-2.36-2.47,0-3.46,1.5-3.11,3.87.17,1.19.25,2.8.32,3.5A10.07,10.07,0,0,1,51,56.1c-.37.38-1.27,2-1.8,2.09s-.14-2.47-1.62-4.69c-1.16-1.75-1.7-1.37-1.7-1.37C43.56,54,47,62.25,50,61.7s.55,8.1,5.37,12.31l.08.08C60.13,78.08,61.72,79.42,67.41,79.23Z" style="fill:#f9d2b5"/><path d="M47.61,53.5c1.48,2.22,1.18,4.8,1.62,4.69s1.43-1.71,1.8-2.09A10.07,10.07,0,0,0,52.89,50c-.07-.7-.15-2.31-.32-3.5-.35-2.37.64-3.83,3.11-3.87,2.14,0,3.5,1.32,4.91,2.36,6.88,5.05,15.4,3.79,23.66,1.37-.24,4.19-.5,7.78.69,11.81.88.05.53-2.25,1.42-3.74.79-1.32,1.63-2.29,2.66-2.2a2.87,2.87,0,0,1,.38.08c2.46-5.22,2.72-13.7.29-18.93s-6.3-7.68-10.14-12c.35,1.18-1.24,2.95-2.75,3.08-1.26.12-3.35-.56-4.61-.71-6.36-.77-10-1.06-15.18.77a19.36,19.36,0,0,0-7.22,4.22,19.88,19.88,0,0,0-5.18,10.65,26.75,26.75,0,0,0-.19,6.38c.12,1.14.81,5.36,1.49,6.38C45.91,52.13,46.45,51.75,47.61,53.5Z" style="fill:#554c44"/></svg>
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1668066300806" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9887" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M1004.836571 955.245714s-10.093714 25.892571-23.405714 0c0 0-149.430857-450.706286-513.536-337.188571v139.922286s-5.851429 82.285714-77.604571 28.672L32.475429 477.622857s-75.922286-41.252571 4.534857-97.865143L398.848 68.900571s54.418286-38.765714 67.584 24.868572l0.438857 150.893714c-0.146286 0.146286 678.473143 32.694857 537.965714 710.656z" p-id="9888" fill="#e43232"></path></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" class="feather feather-cast"><path d="M2 16.1A5 5 0 0 1 5.9 20M2 12.05A9 9 0 0 1 9.95 20M2 8V6a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2h-6"></path><line x1="2" y1="20" x2="2.01" y2="20"></line></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chrome"><circle cx="12" cy="12" r="10"></circle><circle cx="12" cy="12" r="4"></circle><line x1="21.17" y1="8" x2="12" y2="8"></line><line x1="3.95" y1="6.06" x2="8.54" y2="14"></line><line x1="10.88" y1="21.94" x2="15.46" y2="14"></line></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" class="feather feather-copy"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" class="feather feather-heart"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" data-name="Layer 1" width="899.86614" height="731.19068" viewBox="0 0 899.86614 731.19068" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M406.49731,547.15369a10.05574,10.05574,0,0,1-5.12556-14.5425l-23.07542-27.2856,18.43836-2.20463,19.086,26.24886a10.11028,10.11028,0,0,1-9.32339,17.78387Z" transform="translate(-150.06693 -84.40466)" fill="#ffb6b6"/><polygon points="161.428 718.482 173.687 718.481 179.52 671.193 161.425 671.194 161.428 718.482" fill="#ffb6b6"/><path d="M308.36722,798.88372l3.4508-.00013,13.47243-5.47893,7.22081,5.47809h.001A15.38731,15.38731,0,0,1,347.89872,814.269v.5l-39.53077.00146Z" transform="translate(-150.06693 -84.40466)" fill="#2f2e41"/><polygon points="208.937 710.756 220.896 713.456 237.001 668.614 219.351 664.629 208.937 710.756" fill="#ffb6b6"/><path d="M356.835,790.56746l3.36606.76,14.3484-2.37667,5.83675,6.93412.00095.00021a15.3873,15.3873,0,0,1,11.61929,18.39761l-.11014.48771-38.56009-8.70633Z" transform="translate(-150.06693 -84.40466)" fill="#2f2e41"/><path d="M289.05029,559.64482l11.08632,124.20931s-4.15391,74.56955,9.25,100.81h19.4393l9.6247-107.62,1.20861-72.23521L381.335,665.75111,354.79045,782.90466l23.9252,2.13884,43.0748-127.13884L374.46727,538.55115Z" transform="translate(-150.06693 -84.40466)" fill="#2f2e41"/><circle cx="150.59421" cy="248.84861" r="28.59081" fill="#ffb8b8"/><path d="M360.68185,389.0897,327.466,379.02561l-4.32255-13.29253-35.62041,5.78181-7.56821,12.9766-24.49947,22.78084L284.15933,537.0582l-.86888,28.34646s18.3713,8.27056,98.04459-10.77166c.95541-.22834-1.04459-1.22834-1.04459-1.22834Z" transform="translate(-150.06693 -84.40466)" fill="#3f3d56"/><path d="M392.22648,532.15466,353.29,480.015l-11.03833-92.40576.322-.15528a20.18152,20.18152,0,0,1,28.91992,17.373l2.29517,57.10108,38.67041,51.03125Z" transform="translate(-150.06693 -84.40466)" fill="#3f3d56"/><path d="M270.93107,349.47631q-.29881-.40778-.59179-.82245c-.07862.166-.14112.33753-.231.49964Z" transform="translate(-150.06693 -84.40466)" fill="#2f2e41"/><path d="M329.10148,312.34027a18.24293,18.24293,0,0,0-9.54541-9.2215,17.41677,17.41677,0,0,0-13.0581.01141c-3.77979-2.38806-7.97949-4.28125-12.40235-4.51574s-9.09912,1.40637-11.8247,5.01831a19.30233,19.30233,0,0,1-2.06592,2.6839c-2.19092,2.02893-5.52051,1.73626-8.43262,1.32141q1.95923,1.67916,3.91944,3.35833-2.37159.61955-4.74366,1.23914,2.76783,1.67862,5.53516,3.35724c-5.35645,3.215-7.791,10.2323-7.14795,16.60547.6084,6.03387,3.52149,11.55145,7.00391,16.45562,1.38916-2.93151,1.08984-6.43927,1.06689-9.741-.02392-3.48859.59326-7.45,3.38867-9.39972,1.88575-1.31519,4.31055-1.3739,6.58448-1.36145a302.50731,302.50731,0,0,1,34.3164,2.14233,9.66022,9.66022,0,0,1,4.39356,1.27429c2.51855,1.7182,3.00927,5.264,3.2622,8.37482a60.68186,60.68186,0,0,0,1.937-15.73083A28.995,28.995,0,0,0,329.10148,312.34027Z" transform="translate(-150.06693 -84.40466)" fill="#2f2e41"/><circle cx="176.22352" cy="530" r="45" fill="#f0f0f0"/><path d="M326.29045,568.40466a46.24694,46.24694,0,0,0-6.91016.52c-1.16992.17-2.30957.38995-3.43994.66a45.97706,45.97706,0,0,0-19.48974,79.8c.77.65,1.5498,1.28,2.35986,1.88a45.99007,45.99007,0,1,0,27.48-82.86Zm0,90a43.74633,43.74633,0,0,1-26-8.53c-.81983-.6-1.60986-1.22-2.37988-1.88a43.974,43.974,0,0,1,19.62011-76.71c1.11963-.23,2.23975-.41,3.37989-.55a43.52969,43.52969,0,0,1,5.37988-.33,44,44,0,1,1,0,88Z" transform="translate(-150.06693 -84.40466)" fill="#3f3d56"/><rect x="301.29045" y="599.40466" width="50" height="6" rx="3" transform="translate(502.51397 1120.40466) rotate(-180)" fill="#cb8e49"/><rect x="301.29045" y="611.40466" width="50" height="6" rx="3" transform="translate(502.51397 1144.40466) rotate(-180)" fill="#cb8e49"/><rect x="301.29045" y="623.40466" width="50" height="6" rx="3" transform="translate(502.51397 1168.40466) rotate(180)" fill="#cb8e49"/><path d="M329.17019,582.17854a10.05577,10.05577,0,0,1-7.98-13.19378l-28.149-22.014,17.6029-5.91371,24.032,21.8115a10.11027,10.11027,0,0,1-5.506,19.31Z" transform="translate(-150.06693 -84.40466)" fill="#ffb6b6"/><path d="M272.79045,406.90466l-17.33506.36767a33.07345,33.07345,0,0,0-10.66494,24.63233v81.90945l65.04814,54.38381,21.03376-20.36807-48.416-52.74862Z" transform="translate(-150.06693 -84.40466)" fill="#3f3d56"/><rect x="256.00448" y="127.5727" width="643.86166" height="412.35761" fill="#e6e6e6"/><rect x="274.41555" y="179.29263" width="607.03946" height="336.24257" fill="#fff"/><rect x="255.72954" y="127.20769" width="643.86163" height="27.3536" fill="#cb8e49"/><circle cx="276.05655" cy="141.1923" r="5.06978" fill="#fff"/><circle cx="295.30016" cy="141.1923" r="5.06979" fill="#fff"/><circle cx="314.54378" cy="141.1923" r="5.06979" fill="#fff"/><rect x="512.47915" y="214.18052" width="129.9838" height="73.34799" fill="#cb8e49"/><rect x="512.47915" y="307.95454" width="129.9838" height="78.91873" fill="#e6e6e6"/><rect x="670.31658" y="214.18052" width="155.98053" height="116.12476" fill="#e6e6e6"/><rect x="670.31658" y="364.52254" width="155.98053" height="116.12476" fill="#cb8e49"/><rect x="330.31658" y="214.18052" width="155.98053" height="116.12476" fill="#e6e6e6"/><polygon points="621.208 79.642 576.774 246.796 578.81 247.337 622.827 81.748 621.208 79.642" fill="#3f3d56"/><polygon points="749.464 421.592 705.447 587.182 707.066 589.288 751.5 422.133 749.464 421.592" fill="#3f3d56"/><path d="M633.29045,485.052h-150a3.5,3.5,0,0,1,0-7h150a3.5,3.5,0,0,1,0,7Z" transform="translate(-150.06693 -84.40466)" fill="#e6e6e6"/><path d="M633.29045,465.052h-150a3.5,3.5,0,0,1,0-7h150a3.5,3.5,0,0,1,0,7Z" transform="translate(-150.06693 -84.40466)" fill="#e6e6e6"/><path d="M633.29045,505.052h-150a3.5,3.5,0,0,1,0-7h150a3.5,3.5,0,0,1,0,7Z" transform="translate(-150.06693 -84.40466)" fill="#e6e6e6"/><path d="M633.29045,525.052h-150a3.5,3.5,0,1,1,0-7h150a3.5,3.5,0,0,1,0,7Z" transform="translate(-150.06693 -84.40466)" fill="#e6e6e6"/><path d="M633.29045,545.052h-150a3.5,3.5,0,1,1,0-7h150a3.5,3.5,0,0,1,0,7Z" transform="translate(-150.06693 -84.40466)" fill="#e6e6e6"/><path d="M633.29045,565.052h-150a3.5,3.5,0,1,1,0-7h150a3.5,3.5,0,0,1,0,7Z" transform="translate(-150.06693 -84.40466)" fill="#e6e6e6"/><path d="M789.538,505.052h-124a3.5,3.5,0,0,1,0-7h124a3.5,3.5,0,0,1,0,7Z" transform="translate(-150.06693 -84.40466)" fill="#e6e6e6"/><path d="M789.538,525.052h-124a3.5,3.5,0,1,1,0-7h124a3.5,3.5,0,0,1,0,7Z" transform="translate(-150.06693 -84.40466)" fill="#e6e6e6"/><path d="M789.538,545.052h-124a3.5,3.5,0,1,1,0-7h124a3.5,3.5,0,0,1,0,7Z" transform="translate(-150.06693 -84.40466)" fill="#e6e6e6"/><path d="M789.538,565.052h-124a3.5,3.5,0,1,1,0-7h124a3.5,3.5,0,0,1,0,7Z" transform="translate(-150.06693 -84.40466)" fill="#e6e6e6"/><circle cx="624.22352" cy="46" r="45" fill="#f0f0f0"/><path d="M774.29045,84.40466a46.24694,46.24694,0,0,0-6.91016.52c-1.16992.17-2.30957.38995-3.43994.66a45.97706,45.97706,0,0,0-19.48974,79.8c.77.65,1.5498,1.28,2.35986,1.88a45.99007,45.99007,0,1,0,27.48-82.86Zm0,90a43.74633,43.74633,0,0,1-26-8.53c-.81983-.6-1.60986-1.22-2.37988-1.88a43.974,43.974,0,0,1,19.62011-76.71c1.11963-.23,2.23975-.41,3.37989-.55a43.52969,43.52969,0,0,1,5.37988-.33,44,44,0,0,1,0,88Z" transform="translate(-150.06693 -84.40466)" fill="#3f3d56"/><rect x="749.29045" y="115.40466" width="50" height="6" rx="3" transform="translate(1398.51397 152.40466) rotate(-180)" fill="#cb8e49"/><rect x="749.29045" y="127.40466" width="50" height="6" rx="3" transform="translate(1398.51397 176.40466) rotate(-180)" fill="#cb8e49"/><rect x="749.29045" y="139.40466" width="50" height="6" rx="3" transform="translate(1398.51397 200.40466) rotate(180)" fill="#cb8e49"/><circle cx="700.22352" cy="628" r="45" fill="#f0f0f0"/><path d="M850.29045,666.40466a46.24694,46.24694,0,0,0-6.91016.52c-1.16992.17-2.30957.38995-3.43994.66a45.97706,45.97706,0,0,0-19.48974,79.8c.77.65,1.5498,1.28,2.35986,1.88a45.99007,45.99007,0,1,0,27.48-82.86Zm0,90a43.74633,43.74633,0,0,1-26-8.53c-.81983-.6-1.60986-1.22-2.37988-1.88a43.974,43.974,0,0,1,19.62011-76.71c1.11963-.23,2.23975-.41,3.37989-.55a43.52969,43.52969,0,0,1,5.37988-.33,44,44,0,0,1,0,88Z" transform="translate(-150.06693 -84.40466)" fill="#3f3d56"/><rect x="825.29045" y="697.40466" width="50" height="6" rx="3" transform="translate(1550.51397 1316.40466) rotate(-180)" fill="#cb8e49"/><rect x="825.29045" y="709.40466" width="50" height="6" rx="3" transform="translate(1550.51397 1340.40466) rotate(-180)" fill="#cb8e49"/><rect x="825.29045" y="721.40466" width="50" height="6" rx="3" transform="translate(1550.51397 1364.40466) rotate(180)" fill="#cb8e49"/><path d="M497.55163,815.59534h-346.294a1.19069,1.19069,0,0,1,0-2.38137h346.294a1.19069,1.19069,0,0,1,0,2.38137Z" transform="translate(-150.06693 -84.40466)" fill="#cacaca"/></svg>
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--vscode-icons" width="32" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path fill="#41b883" d="M24.4 3.925H30l-14 24.15L2 3.925h10.71l3.29 5.6l3.22-5.6Z"></path><path fill="#41b883" d="m2 3.925l14 24.15l14-24.15h-5.6L16 18.415L7.53 3.925Z"></path><path fill="#35495e" d="M7.53 3.925L16 18.485l8.4-14.56h-5.18L16 9.525l-3.29-5.6Z"></path></svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--vscode-icons" width="32" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path fill="#41b883" d="M24.4 3.925H30l-14 24.15L2 3.925h10.71l3.29 5.6l3.22-5.6Z"></path><path fill="#41b883" d="m2 3.925l14 24.15l14-24.15h-5.6L16 18.415L7.53 3.925Z"></path><path fill="#35495e" d="M7.53 3.925L16 18.485l8.4-14.56h-5.18L16 9.525l-3.29-5.6Z"></path></svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="text-30px iconify iconify--logos" width="0.83em" height="1em" viewBox="0 0 256 312"><path fill="#4C9717" d="M181.252 154.622c.173-1.039.268-2.09.283-3.143v98.197l-51.453-47.052l-25.886-23.607l-15.582-14.324l-6.927-6.323l-2.552-2.33l-.185-.16c-.629-.506-1.233-1.036-1.812-1.517c-.58-.48-1.085-.9-1.775-.69a1.54 1.54 0 0 0-.974 1.11V62.33l.53.481l10.564 9.553c5.56 4.931 11.169 9.862 16.716 14.793a3585.593 3585.593 0 0 1 23.594 21.326c6.204 5.637 12.4 11.283 18.589 16.937c7.396 6.743 14.693 13.56 22.09 20.253c3.328 3.008 6.68 5.954 10.046 8.912c.715.629 1.418 1.233 2.145 1.874c.5.363 1.15.447 1.726.222c.341-.19.59-.51.69-.888c.099-.468.099-.789.173-1.17Z"></path><path fill="#5FBC21" d="M74.278 155.325c-.14 1.12-.18 2.25-.123 3.378c0 31.433 0 62.867.234 94.289v11.192a18.306 18.306 0 0 1-7.236 13.671c-8.394 7.014-16.222 14.681-24.32 22.053c-3.39 3.082-6.756 6.163-10.245 9.171c-3.488 3.008-12.462 3.378-17.43-.776a96.654 96.654 0 0 1-10.527-10.676a15.73 15.73 0 0 1-4.598-10.712c0-.887-.074-1.861 0-2.933c0-1.85 0-3.699.062-5.56c.061-5.3 0-10.613 0-15.914V89.635a15.547 15.547 0 0 1 .468-3.92c.327-1.115.74-2.203 1.233-3.255a27.317 27.317 0 0 1 2.49-4.413l19.723-19.723L36.57 45.775a9.664 9.664 0 0 1 2.268-1.7a9.923 9.923 0 0 1 3.957-1.234c4.126-.76 8.386.026 11.97 2.207c.919.56 1.781 1.209 2.576 1.935l17.048 15.31v92.453a4.05 4.05 0 0 0-.11.579Zm179.123 75.453l-36.229 33.283c-.142.107-.29.206-.443.296l-.136.099l-.247.148a17.258 17.258 0 0 1-20.66-2.034l-2.181-1.911l-12.02-10.983V46.453a64.03 64.03 0 0 1 .827-5.103c.863-4.376 4.709-6.546 7.642-9.282c6.373-5.917 12.944-11.612 19.44-17.406c4.475-3.994 8.937-8 13.449-11.97c3.932-3.463 11.55-3.29 15.248-1.146a12.41 12.41 0 0 1 2.564 1.985l4.167 4.018l3.365 3.218l3.07 2.97a14.114 14.114 0 0 1 2.194 2.577a14.46 14.46 0 0 1 2.095 7.963c-.061 18.071.074 36.143.111 54.239c.074 30.694.14 61.388.197 92.082c0 15.73.03 31.454.087 47.175v2.158c.185 4.684.086 7.778-2.54 10.847Z"></path></svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="text-30px iconify iconify--noto" width="1em" height="1em" viewBox="0 0 128 128"><path fill="#FFBE1F" d="M64.36 54.09c-15.61-.56-28.51 9.61-31.55 29.94c-3.06 20.45 6.76 40.38 31.26 39.93c26.04-.47 34.07-21.65 30.62-41.26c-3.94-22.28-16.97-28.12-30.33-28.61z"></path><path fill="#F79429" d="M77.01 72.23c-2.19-.06-6.91 4.64-7.85 5.69c-1.9 2.13-.5 4.24 1.69 3.18c2.11-1.02 5.38-3.28 6.2-3.29c.88-.01 3.42 1.67 4.49 2.48c3.08 2.3 4.79-.11 2.9-2.42c-1.06-1.29-4.92-5.57-7.43-5.64zm-26.09.06c-2.5-.13-8.08 5.56-8.38 5.88c-1.59 1.74-.11 3.75 2.01 2.85c1.22-.52 5.08-3.1 6.01-3.07c.69.02 2.19 1.14 4.35 2.55c3.54 2.3 5.49-.08 3.15-2.62c-2.36-2.56-4.97-5.48-7.14-5.59zM40.09 86.26c-1.45-.1-4.41 2.6-5.24 3.52c-.83.92-1.5 1.73-.87 2.7c.43.66 1.78.69 2.8.11c1.31-.74 2.66-1.47 3.17-1.45c.83.04 4.12 2.95 5.14 3.82c2.66 2.25 4.35-.77 2.88-2.51c-1.57-1.86-5.2-6-7.88-6.19zm24.04.24c-2.78-.04-7.96 5.49-8.75 6.41c-1.91 2.23-.46 5.2 2.51 3.12c.89-.63 4.63-3.84 5.98-3.74c.86.07 3.79 2.37 5.8 3.78c2.68 1.89 4.49-.29 2.64-2.77c-.83-1.12-5.11-6.76-8.18-6.8zm23.22-.63c-1.61-.09-7.02 5.6-7.94 6.8c-1.59 2.08.15 4.06 2.59 2.33c1.73-1.23 5.32-3.94 5.69-3.96c.53-.04 1.95 1.03 3.04 1.66c1.17.68 2.51.62 2.92-.15c.56-1.04-.27-2.06-1.12-2.85c-1.42-1.34-3.78-3.75-5.18-3.83zM76.5 101.91c-2.9.05-7.07 4.98-7.83 6.25c-1.45 2.44.78 3.46 2.03 2.84c1.89-.95 3.93-3.16 5.95-3.53c.87-.16 3.69 2.07 4.43 2.53c3.68 2.3 5.01-.81 2.58-3.22c-1.75-1.73-5.07-4.91-7.16-4.87zm-24.74-.09c-2.04-.1-6.92 4.33-8.14 5.51c-1.64 1.6-.45 4.26 2.15 3c2.27-1.1 4.99-2.78 5.75-2.74c.96.05 3.03 1.66 4.26 2.5c3.66 2.52 5.24-.25 2.96-2.89c-1.63-1.87-4.78-5.27-6.98-5.38z"></path><path fill="#2F7C31" d="M25.53 23.68c-.91-1.17 4.1-3.87 9.38-4.02c6.63-.19 12.71 1.57 12.71 1.57s.55-4.36-2.95-9.24c-2.4-3.35-3.49-5.01-3.42-6.2c.07-1.2 5.27-1.93 12.2 1.16c5.79 2.58 10.29 7.5 10.29 7.5s3.66-5.12 8.48-7.59C77.82 4 84.68 3.51 85.28 4.64c.46.86-1.72 3.38-3.15 6.62c-2.25 5.08-2.12 9.27-2.12 9.27s4.66-3.4 9.66-4.4c2.54-.51 10.03-.97 9.53.86c-.08.29-12.88 9.28-12.88 9.28l-1.91 9.35s6.79-3.88 12.84-3.17c3.13.37 2.61 1.35 2.61 1.35l-14.52 8.24l-2.16 7.13s4.07-1.02 8.03-1.07c1.91-.02 5.04.48 5.19 1.45c.2 1.25-8.75 5.67-8.75 5.67l-24.09 9.76l-22.87-8.28s-10.27-1.62-10.27-2.74c0-1.13 2.05-2.01 5.13-2.49c3.56-.56 7.22-.36 7.22-.36L32.2 44.1s-6.5-.61-7.06-1.88c-.56-1.27 2.92-3.59 5.05-4.21c5.39-1.58 11.53-.32 11.53-.32l-3.47-9.29c-.02-.03-11.73-3.45-12.72-4.72z"></path><path fill="#709921" d="M54.36 33.59c.54.27 1.94-3.05 3.67-5.18c2.52-3.11 5.8-5.44 6.37-5.44c.55 0 3.12 1.95 5.35 4.62c2.7 3.24 3.91 5.95 4.18 6.08c1 .49 4.24-5.22 10.48-10.04c5.39-4.17 14.34-7.9 14.81-6.64c.15.41-4.44 3.43-8.84 9.55c-4.83 6.71-4.36 13.94-4.36 13.94s5.53-3.97 7.31-4.89c1.49-.77 7.14-2.94 6.5-1.73c-.46.86-5.86 6.24-8.96 9.19c-4.88 4.62-6.32 11.51-5.85 11.9c.54.45 4.17-2.24 6.36-3.47c2.08-1.17 5.02-2.12 5-1.92c-.13 1.47-5.43 5.29-9.16 8.64c-2.76 2.47-8.1 9.52-23.31 9.27c-12.57-.2-19.92-6.78-24.5-9.24c-3.16-1.7-9.04-3.6-8.97-4.18c.04-.32 2.8-.54 4.66-.31c5.14.62 8.44 2.17 8.79 1.72c.36-.46-4.55-6.68-13.03-10.46c-4.09-1.83-5.88-2.3-5.8-3.12c.03-.36 2.59-.83 5.85-.94c5.78-.19 11.28 1.85 11.79 1.91c1.2.15-3.65-8.26-6.4-11.54c-4.36-5.21-11.16-6.6-10.88-7.91c.15-.71 8.07-.81 14.57 1.63c6.77 2.52 13.2 7.98 14.37 8.56z"></path><path fill="#2F7C31" d="M64.38 38.24c-.61.08-3.02 1.38-6.19 4.71c-3.75 3.93-5.07 8.18-4.2 8.9c1.17.96 2.8-.87 5.04-2.45c2.64-1.86 4.78-2.87 5.24-2.87c.46 0 2.52 1.3 4.25 2.61c2.15 1.64 4.01 3.41 4.96 2.81c1.6-.99-.62-6.17-3.12-9c-2.66-3.02-5.45-4.78-5.98-4.71z"></path></svg>
<svg xmlns='http://www.w3.org/2000/svg' width='361' height='361' viewBox='0 0 200 200'><rect fill='#F4F7F9' width='200' height='200'/><defs><linearGradient id='a' gradientUnits='userSpaceOnUse' x1='100' y1='33' x2='100' y2='-3'><stop offset='0' stop-color='#000' stop-opacity='0'/><stop offset='1' stop-color='#000' stop-opacity='1'/></linearGradient><linearGradient id='b' gradientUnits='userSpaceOnUse' x1='100' y1='135' x2='100' y2='97'><stop offset='0' stop-color='#000' stop-opacity='0'/><stop offset='1' stop-color='#000' stop-opacity='1'/></linearGradient></defs><g fill='#cbced0' fill-opacity='0.08'><rect x='100' width='100' height='100'/><rect y='100' width='100' height='100'/></g><g fill-opacity='0.08'><polygon fill='url(#a)' points='100 30 0 0 200 0'/><polygon fill='url(#b)' points='100 100 0 130 0 100 200 100 200 130'/></g></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="text-30px iconify iconify--logos" width="1em" height="1em" viewBox="0 0 256 256"><path fill="#3178C6" d="M20 0h216c11.046 0 20 8.954 20 20v216c0 11.046-8.954 20-20 20H20c-11.046 0-20-8.954-20-20V20C0 8.954 8.954 0 20 0Z"></path><path fill="#FFF" d="M150.518 200.475v27.62c4.492 2.302 9.805 4.028 15.938 5.179c6.133 1.151 12.597 1.726 19.393 1.726c6.622 0 12.914-.633 18.874-1.899c5.96-1.266 11.187-3.352 15.678-6.257c4.492-2.906 8.048-6.704 10.669-11.394c2.62-4.689 3.93-10.486 3.93-17.391c0-5.006-.749-9.394-2.246-13.163a30.748 30.748 0 0 0-6.479-10.055c-2.821-2.935-6.205-5.567-10.149-7.898c-3.945-2.33-8.394-4.531-13.347-6.602c-3.628-1.497-6.881-2.949-9.761-4.359c-2.879-1.41-5.327-2.848-7.342-4.316c-2.016-1.467-3.571-3.021-4.665-4.661c-1.094-1.64-1.641-3.495-1.641-5.567c0-1.899.489-3.61 1.468-5.135s2.362-2.834 4.147-3.927c1.785-1.094 3.973-1.942 6.565-2.547c2.591-.604 5.471-.906 8.638-.906c2.304 0 4.737.173 7.299.518c2.563.345 5.14.877 7.732 1.597a53.669 53.669 0 0 1 7.558 2.719a41.7 41.7 0 0 1 6.781 3.797v-25.807c-4.204-1.611-8.797-2.805-13.778-3.582c-4.981-.777-10.697-1.165-17.147-1.165c-6.565 0-12.784.705-18.658 2.115c-5.874 1.409-11.043 3.61-15.506 6.602c-4.463 2.993-7.99 6.805-10.582 11.437c-2.591 4.632-3.887 10.17-3.887 16.615c0 8.228 2.375 15.248 7.127 21.06c4.751 5.811 11.963 10.731 21.638 14.759a291.458 291.458 0 0 1 10.625 4.575c3.283 1.496 6.119 3.049 8.509 4.66c2.39 1.611 4.276 3.366 5.658 5.265c1.382 1.899 2.073 4.057 2.073 6.474a9.901 9.901 0 0 1-1.296 4.963c-.863 1.524-2.174 2.848-3.93 3.97c-1.756 1.122-3.945 1.999-6.565 2.632c-2.62.633-5.687.95-9.2.95c-5.989 0-11.92-1.05-17.794-3.151c-5.875-2.1-11.317-5.25-16.327-9.451Zm-46.036-68.733H140V109H41v22.742h35.345V233h28.137V131.742Z"></path></svg>
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1713147025406" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4286" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M747.2 896H276.8c-46.4 0-84.8-32-94.4-76.8L64 230.4c-3.2-12.8 3.2-27.2 16-33.6 11.2-6.4 27.2-4.8 36.8 3.2l198.4 169.6L486.4 140.8c12.8-16 38.4-16 51.2 0l171.2 228.8 198.4-169.6c9.6-9.6 25.6-9.6 36.8-3.2 11.2 6.4 17.6 20.8 16 33.6l-118.4 588.8c-9.6 44.8-48 76.8-94.4 76.8zM145.6 308.8l99.2 497.6c3.2 14.4 16 25.6 32 25.6h470.4c16 0 28.8-11.2 32-25.6l99.2-497.6-153.6 131.2c-6.4 6.4-16 8-24 8-8-1.6-17.6-4.8-22.4-12.8L512 212.8 345.6 435.2c-4.8 6.4-12.8 11.2-22.4 12.8-8 1.6-17.6-1.6-24-8l-153.6-131.2z" fill="#333333" p-id="4287"></path><path d="M512 736c-11.2 0-22.4-6.4-27.2-16l-96-160c-9.6-14.4-4.8-35.2 11.2-43.2 14.4-9.6 35.2-4.8 43.2 11.2l68.8 113.6 68.8-113.6c9.6-14.4 28.8-20.8 43.2-11.2 14.4 9.6 20.8 28.8 11.2 43.2l-96 160c-4.8 9.6-16 16-27.2 16z" fill="#333333" p-id="4288"></path></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="text-30px iconify iconify--logos" width="1em" height="1em" viewBox="0 0 256 257"><defs><linearGradient id="iconifyVue28" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="iconifyVue29" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#iconifyVue28)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#iconifyVue29)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="text-30px iconify iconify--logos" width="1.16em" height="1em" viewBox="0 0 256 221"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" class="feather feather-wind"><path d="M9.59 4.59A2 2 0 1 1 11 8H2m10.59 11.41A2 2 0 1 0 14 16H2m15.73-8.27A2.5 2.5 0 1 1 19.5 12H2"></path></svg>
\ No newline at end of file
<svg fill="#48B0F1" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Windi CSS</title><path d="M4.12 15.561H.96v2.431h3.16zm13.728 4.22A4.224 4.224 0 0113.628 24c-2.452 0-3.918-1.838-3.918-3.617h2.412c0 .5.467 1.206 1.506 1.206a1.81 1.81 0 001.809-1.809c0-.997-.917-1.808-2.563-1.808H5.589v-2.411h7.285c3.097 0 4.974 1.893 4.974 4.22zA4.224 4.224 0 0113.628 24c-2.452 0-3.918-1.838-3.918-3.617h2.412c0 .5.467 1.206 1.506 1.206a1.81 1.81 0 001.809-1.809c0-.997-.917-1.808-2.563-1.808H5.589v-2.411h7.285c3.097 0 4.974 1.893 4.974 4.22zM18.48 3.72c-2.66 0-4.536 2.022-4.536 4.682h2.136c0-1.322.96-2.282 2.4-2.282s2.16.96 2.16 2.282-.935 2.411-3.48 2.411H.96v2.411h16.56c3.769 0 5.52-2.422 5.52-4.822 0-2.66-1.8-4.682-4.56-4.682zm-5.981.5A4.224 4.224 0 008.279 0C5.827 0 4.361 1.838 4.361 3.617h2.411c0-.5.468-1.206 1.507-1.206a1.81 1.81 0 011.809 1.809c0 .997-.728 1.808-2.563 1.808H.96v2.411h6.565c3.097 0 4.974-1.893 4.974-4.22zA4.224 4.224 0 008.279 0C5.827 0 4.361 1.838 4.361 3.617h2.411c0-.5.468-1.206 1.507-1.206a1.81 1.81 0 011.809 1.809c0 .997-.728 1.808-2.563 1.808H.96v2.411h6.565c3.097 0 4.974-1.893 4.974-4.22z"/></svg>
\ No newline at end of file
<template>
<n-tag v-if="props.status === COMMON_ENABLE" size="small" strong :bordered="false" type="success">{{
filterStatus(props.status)
}}</n-tag>
<n-tag v-if="props.status === COMMON_DISABLE" size="small" :bordered="false" disabled>{{
filterStatus(props.status)
}}</n-tag>
</template>
<script setup lang="ts">
import { COMMON_DISABLE, COMMON_ENABLE } from '@/const';
import { filterStatus } from '@/filter';
interface Props {
status: string;
}
const props = defineProps<Props>();
</script>
<template>
<n-tag v-if="props.topFlag === Boolean(DISCOVER_TOPFLAG_TRUE)" size="small" strong :bordered="false" type="success"
></n-tag
>
<n-tag v-if="props.topFlag === Boolean(DISCOVER_TOPFLAG_FALSE)" size="small" :bordered="false" disabled></n-tag>
</template>
<script setup lang="ts">
import { DISCOVER_TOPFLAG_FALSE, DISCOVER_TOPFLAG_TRUE } from '@/const';
interface Props {
topFlag: boolean;
}
const props = defineProps<Props>();
</script>
<template>
<div class="gm__upload--image">
<n-space class="images--wrap">
<div
v-for="(item, index) in filePathList"
:key="index"
class="image__wrap w-120px h-120px border-1px border-solid rounded-md overflow-hidden"
>
<n-image class="w-full h-full" :object-fit="props.imgMode" :src="item" />
<div v-if="!disabled" class="mask" @click="handleDelImage(index)">
<Icon class="text-24px" icon="ic:baseline-delete-outline" />
</div>
</div>
<div v-if="!isGreaterThenMaximum && !props.disabled" class="upload--button" @click="handleOpenChooseFile">
<Icon class="text-24px" icon="ic:baseline-add" />
</div>
</n-space>
<input
ref="upload"
:accept="acceptTypes"
multiple
type="file"
name="upload"
style="display: none"
@change="handleGetFileData"
/>
</div>
</template>
<script setup lang="ts">
import { ref, nextTick, computed, watch } from 'vue';
import { Icon } from '@iconify/vue';
import { fetchUploadImgFile } from '@/service';
const emits = defineEmits(['update:file', 'update:files']);
interface Props {
file?: string /** 单个文件 */;
files?: any /** 多个文件 */;
acceptTypes?: string /** 允许上传的图片类型 默认 'image/jpeg, image/jpg, image/png' */;
max?: number /** 最多允许上传多少张图 */;
disabled: boolean /** 是否禁用 */;
imgMode: 'fill' | 'none' | 'contain' | 'cover' | 'scale-down';
/** 场景 */
scene: string;
}
const props = withDefaults(defineProps<Props>(), {
acceptTypes: 'image/jpeg, image/jpg, image/png, image/gif',
max: 3,
file: '',
files: null,
disabled: false,
imgMode: 'cover',
scene: ''
});
const filePathList = ref<string[]>([]);
const upload = ref();
watch(
() => props.file,
async val => {
if (val) {
filePathList.value.length = 0;
filePathList.value.push(val);
}
},
{
immediate: true,
deep: true
}
);
/** 是否超出最大值 */
const isGreaterThenMaximum = computed(() => {
return filePathList.value.length >= props.max || props.file !== '';
});
/** 选择文件 */
function handleOpenChooseFile() {
if (props.disabled) return;
upload.value.click();
}
/** 获取文件数据 */
function handleGetFileData() {
nextTick(() => {
const files = [...upload.value.files];
files.forEach(file => {
const isOk = beforeUpload(file);
if (isOk) hanldeUpload(file);
});
});
}
/** 上传前的校验 */
function beforeUpload(file: File) {
const isAccept = props.acceptTypes.includes(file.type);
if (!isAccept) {
window.$message?.error(`${file.name} 格式有误!`);
return false;
}
const maxFileSize = 5 * 1024 * 1024;
if (file.size > maxFileSize) {
window.$message?.error(`请上传5M以内的图片!`);
return false;
}
return true;
}
/** 处理上传 */
async function hanldeUpload(file: File) {
const formData = new FormData();
formData.append('file', file);
formData.append('scene', props.scene);
const res = await fetchUploadImgFile(formData);
if (res.data?.ok) {
filePathList.value.push(res.data.data.fullUrl);
onUpdate();
}
}
/** 删除图片 */
function handleDelImage(index: number) {
window.$dialog?.info({
title: '提示',
content: '您确定要删除这个图片吗?',
positiveText: '确定',
negativeText: '取消',
onPositiveClick: () => {
filePathList.value.splice(index, 1);
onUpdate();
}
});
}
/** 更新 */
function onUpdate() {
if (props.max === 1) {
emits('update:file', filePathList.value[0]);
} else {
emits('update:files', filePathList.value);
}
}
</script>
<style lang="scss">
.upload--button {
background-color: #f7f9f8;
border: 1px dashed #ccc;
border-radius: 6px;
box-sizing: border-box;
width: 120px;
height: 120px;
cursor: pointer;
vertical-align: top;
display: inline-flex;
justify-content: center;
align-items: center;
transition: border 0.4s;
position: relative;
> svg {
color: gray;
}
&:hover {
border: 1px dashed #666;
}
.disabled {
position: absolute;
left: 0;
right: 0;
width: 100%;
height: 100%;
background-color: rgba(204, 204, 204, 0.5);
}
}
.image__wrap {
position: relative;
cursor: pointer;
&:hover {
.mask {
opacity: 1 !important;
}
}
.mask {
opacity: 0;
width: 35px;
transition: all 0.3s;
height: 35px;
position: absolute;
right: 0;
top: 0;
border-radius: 0 0 0 50%;
background-color: #f56c6c;
display: flex;
justify-content: center;
align-items: center;
> svg {
color: #f7f7f7;
}
}
}
</style>
<template>
<div
class="bg-white text-[#333639] dark:(bg-[#18181c] text-white text-opacity-82) transition-all duration-300 ease-in-out"
>
<slot></slot>
</div>
</template>
<script setup lang="ts"></script>
<style scoped></style>
<template>
<div class="flex-center text-18px hover:text-primary cursor-pointer" @click="handleSwitch">
<icon-mdi-moon-waning-crescent v-if="darkMode" />
<icon-mdi-white-balance-sunny v-else />
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue';
interface Props {
/** 暗黑模式 */
dark?: boolean;
}
interface Emits {
(e: 'update:dark', darkMode: boolean): void;
}
const props = withDefaults(defineProps<Props>(), {
dark: false
});
const emit = defineEmits<Emits>();
const darkMode = computed({
get() {
return props.dark;
},
set(newValue: boolean) {
emit('update:dark', newValue);
}
});
function handleSwitch() {
darkMode.value = !darkMode.value;
}
</script>
<style scoped></style>
<template>
<div v-if="showTooltip">
<n-tooltip :placement="placement" trigger="hover">
<template #trigger>
<div class="flex-center h-full cursor-pointer hover:bg-[#f6f6f6] dark:hover:bg-[#333]" :class="contentClass">
<slot></slot>
</div>
</template>
{{ tooltipContent }}
</n-tooltip>
</div>
<div v-else class="flex-center cursor-pointer hover:bg-[#f6f6f6] dark:hover:bg-[#333]" :class="contentClass">
<slot></slot>
</div>
</template>
<script lang="ts" setup>
import { computed } from 'vue';
import type { FollowerPlacement } from 'vueuc';
interface Props {
/** tooltip显示文本 */
tooltipContent?: string;
/** tooltip的位置 */
placement?: FollowerPlacement;
/** class类 */
contentClass?: string;
}
const props = withDefaults(defineProps<Props>(), {
tooltipContent: '',
placement: 'bottom',
contentClass: ''
});
const showTooltip = computed(() => Boolean(props.tooltipContent));
</script>
<style scoped></style>
<template>
<n-loading-bar-provider>
<n-dialog-provider>
<n-notification-provider>
<n-message-provider>
<slot></slot>
<naive-provider-content />
</n-message-provider>
</n-notification-provider>
</n-dialog-provider>
</n-loading-bar-provider>
</template>
<script setup lang="ts">
import { defineComponent, h } from 'vue';
import { useLoadingBar, useDialog, useMessage, useNotification } from 'naive-ui';
// 挂载naive组件的方法至window, 以便在路由钩子函数和请求函数里面调用
function registerNaiveTools() {
window.$loadingBar = useLoadingBar();
window.$dialog = useDialog();
window.$message = useMessage();
window.$notification = useNotification();
}
const NaiveProviderContent = defineComponent({
setup() {
registerNaiveTools();
},
render() {
return h('div');
}
});
</script>
<style scoped></style>
<template>
<icon-custom-logo-fill v-if="fill" />
<icon-custom-logo v-else />
</template>
<script lang="ts" setup>
interface Props {
/** logo是否填充 */
fill?: boolean;
}
withDefaults(defineProps<Props>(), {
fill: false
});
</script>
<style scoped></style>
<script lang="ts" setup>
import { ref, watch, computed } from 'vue';
import { type ECOption, useEcharts } from '@/composables';
type ChartData = {
seriesData: string[];
xaxis: string[];
};
type Props = {
chartData: ChartData;
xRotate?: number;
itemStyleColor?: string;
};
const props = defineProps<Props>();
const isExceed = computed(() => {
if (JSON.stringify(props.chartData) !== '{}') {
return props?.chartData?.xaxis.length > 15;
}
return 0;
});
function barOptionsCreator() {
const options = {
grid: {
x: 30,
y: 30,
x2: 30,
y2: isExceed.value ? 90 : 30
},
tooltip: {
showContent: true,
trigger: 'axis',
backgroundColor: 'rgba(8,36,68,.9)',
borderColor: 'rgba(8,36,68,.9)',
textStyle: {
color: '#fff'
},
formatter(params: any) {
let relVal = params[0].name;
for (let i = 0, l = params.length; i < l; i++) {
relVal += `<br/>${params[i].marker} ${parseFloat(params[i].value)}%`;
}
return relVal;
}
},
xAxis: [
{
type: 'category',
data: props.chartData?.xaxis || [],
axisLine: {
lineStyle: {
color: '#ddd'
}
},
axisTick: {
show: false
},
axisLabel: {
interval: 0,
rotate: isExceed.value ? 40 : props.xRotate || 0,
color: '#2b3d4f',
margin: 15
}
}
],
yAxis: [
{
type: 'value',
axisLabel: {
color: '#2b3d4f'
},
nameTextStyle: {
color: '#2b3d4f',
fontSize: 12,
lineHeight: 40
},
// 分割线
splitLine: {
lineStyle: {
type: 'dashed',
color: '#E9E9E9'
}
},
axisLine: {
show: false
},
axisTick: {
show: false
}
}
],
series: [
{
type: 'bar',
barMaxWidth: 50,
itemStyle: {
color: props.itemStyleColor || '#E6000A',
opacity: 1
},
data: props.chartData?.seriesData || []
}
]
} as ECOption;
return options;
}
const barOptions = ref<ECOption>(barOptionsCreator());
const { domRef: barRef } = useEcharts(barOptions);
watch(
() => props.chartData,
val => {
if (val) {
barOptions.value = barOptionsCreator();
}
},
{
deep: true,
immediate: true
}
);
</script>
<template>
<div ref="barRef" class="w-full h-315px"></div>
</template>
<template>
<div ref="bsWrap" class="h-full text-left">
<div ref="bsContent" class="inline-block" :class="{ 'h-full': !isScrollY }">
<slot></slot>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed, watch, onMounted } from 'vue';
import { useElementSize } from '@vueuse/core';
import BScroll from '@better-scroll/core';
import type { Options } from '@better-scroll/core';
interface Props {
/** better-scroll的配置: https://better-scroll.github.io/docs/zh-CN/guide/base-scroll-options.html */
options: Options;
}
const props = defineProps<Props>();
const bsWrap = ref<HTMLElement>();
const instance = ref<BScroll>();
const bsContent = ref<HTMLElement>();
const isScrollY = computed(() => Boolean(props.options.scrollY));
function initBetterScroll() {
if (!bsWrap.value) return;
instance.value = new BScroll(bsWrap.value, props.options);
}
// 滚动元素发生变化,刷新BS
const { width: wrapWidth } = useElementSize(bsWrap);
const { width, height } = useElementSize(bsContent);
watch([() => wrapWidth.value, () => width.value, () => height.value], () => {
if (instance.value) {
instance.value.refresh();
}
});
onMounted(() => {
initBetterScroll();
});
defineExpose({ instance });
</script>
<style scoped></style>
<template>
<span>{{ value }}</span>
</template>
<script lang="ts" setup>
import { ref, computed, onMounted, watch, watchEffect } from 'vue';
import { useTransition, TransitionPresets } from '@vueuse/core';
import { isNumber } from '@/utils';
interface Props {
/** 初始值 */
startValue?: number;
/** 结束值 */
endValue?: number;
/** 动画时长 */
duration?: number;
/** 自动动画 */
autoplay?: boolean;
/** 进制 */
decimals?: number;
/** 前缀 */
prefix?: string;
/** 后缀 */
suffix?: string;
/** 分割符号 */
separator?: string;
/** 小数点 */
decimal?: string;
/** 使用缓冲动画函数 */
useEasing?: boolean;
/** 缓冲动画函数类型 */
transition?: string;
}
const props = withDefaults(defineProps<Props>(), {
startValue: 0,
endValue: 2021,
duration: 1000,
autoplay: true,
decimals: 0,
prefix: '',
suffix: '',
separator: ',',
decimal: '.',
useEasing: true,
transition: 'linear'
});
const emit = defineEmits<{
(e: 'on-started'): void;
(e: 'on-finished'): void;
}>();
const source = ref(props.startValue);
let outputValue = useTransition(source);
const value = computed(() => formatNumber(outputValue.value));
const disabled = ref(false);
function run() {
outputValue = useTransition(source, {
disabled,
duration: props.duration,
onStarted: () => emit('on-started'),
onFinished: () => emit('on-finished'),
...(props.useEasing ? { transition: TransitionPresets[props.transition] } : {})
});
}
function start() {
run();
source.value = props.endValue;
}
function formatNumber(num: number | string) {
if (num === null || num === undefined) {
return '';
}
const { decimals, decimal, separator, suffix, prefix } = props;
let number = Number(num).toFixed(decimals);
number += '';
const x = number.split('.');
let x1 = x[0];
const x2 = x.length > 1 ? decimal + x[1] : '';
const rgx = /(\d+)(\d{3})/;
if (separator && !isNumber(separator)) {
while (rgx.test(x1)) {
x1 = x1.replace(rgx, `$1${separator}$2`);
}
}
return prefix + x1 + x2 + suffix;
}
watch([() => props.startValue, () => props.endValue], () => {
if (props.autoplay) {
start();
}
});
watchEffect(() => {
source.value = props.startValue;
});
onMounted(() => {
if (props.autoplay) {
start();
}
});
</script>
<template>
<n-popover placement="bottom-end" trigger="click">
<template #trigger>
<n-input v-model:value="modelValue" readonly placeholder="点击选择图标">
<template #suffix>
<Icon :icon="modelValue ? modelValue : emptyIcon" class="text-30px p-5px" />
</template>
</n-input>
</template>
<template #header>
<n-input v-model:value="searchValue" placeholder="搜索图标"></n-input>
</template>
<div v-if="iconsList.length > 0" class="grid grid-cols-9 h-auto overflow-auto">
<template v-for="iconItem in iconsList" :key="iconItem">
<Icon
:icon="iconItem"
class="border-1px border-[#d9d9d9] text-30px m-2px p-5px"
:style="{ 'border-color': modelValue === iconItem ? theme.themeColor : '' }"
@click="handleChange(iconItem)"
/>
</template>
</div>
<n-empty v-else class="w-306px" description="你什么也找不到" />
</n-popover>
</template>
<script lang="ts" setup>
import { ref, computed } from 'vue';
import { Icon } from '@iconify/vue';
import { useThemeStore } from '@/store';
interface Props {
/** 选中的图标 */
value: string;
/** 图标列表 */
icons: string[];
/** 未选中图标 */
emptyIcon?: string;
}
interface Emits {
(e: 'update:value', val: string): void;
}
const props = withDefaults(defineProps<Props>(), {
emptyIcon: 'mdi:apps'
});
const emit = defineEmits<Emits>();
const theme = useThemeStore();
const searchValue = ref('');
const iconsList = computed(() => props.icons.filter(v => v.includes(searchValue.value)));
const modelValue = computed({
get() {
return props.value;
},
set(val: string) {
emit('update:value', val);
}
});
function handleChange(iconItem: string) {
modelValue.value = iconItem;
}
</script>
<style lang="scss" scoped>
:deep(.n-input-wrapper) {
padding-right: 0;
}
:deep(.n-input__suffix) {
border: 1px solid #d9d9d9;
}
</style>
<script lang="ts" setup>
import { ref, watch } from 'vue';
import { type ECOption, useEcharts } from '@/composables';
import { hexToRgba } from '@/utils/common';
type ChartData = {
seriesData: string[];
xaxis: string[];
};
type Props = {
chartData: ChartData;
lineStyleColor?: string;
};
const props = defineProps<Props>();
function lineOptionsCreator() {
const optoins = {
grid: {
x: 55,
y: 30,
x2: 30,
y2: 30
},
tooltip: {
showContent: true,
trigger: 'axis',
backgroundColor: 'rgba(8,36,68,.9)',
borderColor: 'rgba(8,36,68,.9)',
textStyle: {
color: '#fff'
}
},
xAxis: [
{
type: 'category',
data: props.chartData?.xaxis,
axisLine: {
lineStyle: {
color: '#ddd'
}
},
axisTick: {
show: false
},
axisLabel: {
interval: 5,
color: '#2b3d4f',
margin: 15
}
}
],
yAxis: [
{
type: 'value',
axisLabel: {
color: '#2b3d4f'
},
nameTextStyle: {
color: '#2b3d4f',
fontSize: 12,
lineHeight: 40
},
// 分割线
splitLine: {
lineStyle: {
type: 'dashed',
color: '#E9E9E9'
}
},
axisLine: {
show: false
},
axisTick: {
show: false
}
}
],
series: [
{
name: '金额',
type: 'line',
data: props.chartData?.seriesData,
symbol: 'circle',
showSymbol: false,
smooth: true,
symbolSize: 8, // 设定实心点的大小
lineStyle: {
width: 4,
color: props.lineStyleColor || '#E6000A',
opacity: 0.7,
shadowColor: hexToRgba(props.lineStyleColor || '#E6000A', 0.4), // 设置折线阴影
shadowBlur: 5,
shadowOffsetY: 5
},
itemStyle: {
color: props.lineStyleColor || '#E6000A',
borderColor: '#fff',
borderWidth: 2,
shadowColor: hexToRgba(props.lineStyleColor || '#E6000A', 0.4),
shadowBlur: 10
}
}
]
} as ECOption;
return optoins;
}
const barOptions = ref<ECOption>(lineOptionsCreator());
const { domRef: lineRef } = useEcharts(barOptions);
watch(
() => props.chartData,
val => {
if (val) {
barOptions.value = lineOptionsCreator();
}
},
{
deep: true,
immediate: true
}
);
</script>
<template>
<div ref="lineRef" class="w-full h-315px"></div>
</template>
<template>
<p>
<span>{{ label }}</span>
<a class="text-blue-500" :href="link" target="_blank">
{{ link }}
</a>
</p>
</template>
<script setup lang="ts">
interface Props {
/** 网址名称 */
label: string;
/** 网址链接 */
link: string;
}
defineProps<Props>();
</script>
<style scoped></style>
import { nextTick, onUnmounted, ref, watch } from 'vue';
import type { ComputedRef, Ref } from 'vue';
import * as echarts from 'echarts/core';
import { BarChart, GaugeChart, LineChart, PictorialBarChart, PieChart, RadarChart, ScatterChart } from 'echarts/charts';
import type {
BarSeriesOption,
GaugeSeriesOption,
LineSeriesOption,
PictorialBarSeriesOption,
PieSeriesOption,
RadarSeriesOption,
ScatterSeriesOption
} from 'echarts/charts';
import {
DatasetComponent,
GridComponent,
LegendComponent,
TitleComponent,
ToolboxComponent,
TooltipComponent,
TransformComponent
} from 'echarts/components';
import type {
DatasetComponentOption,
GridComponentOption,
LegendComponentOption,
TitleComponentOption,
ToolboxComponentOption,
TooltipComponentOption
} from 'echarts/components';
import { LabelLayout, UniversalTransition } from 'echarts/features';
import { CanvasRenderer } from 'echarts/renderers';
import { useElementSize } from '@vueuse/core';
import { useThemeStore } from '@/store';
export type ECOption = echarts.ComposeOption<
| BarSeriesOption
| LineSeriesOption
| PieSeriesOption
| ScatterSeriesOption
| PictorialBarSeriesOption
| RadarSeriesOption
| GaugeSeriesOption
| TitleComponentOption
| LegendComponentOption
| TooltipComponentOption
| GridComponentOption
| ToolboxComponentOption
| DatasetComponentOption
>;
echarts.use([
TitleComponent,
LegendComponent,
TooltipComponent,
GridComponent,
DatasetComponent,
TransformComponent,
ToolboxComponent,
BarChart,
LineChart,
PieChart,
ScatterChart,
PictorialBarChart,
RadarChart,
GaugeChart,
LabelLayout,
UniversalTransition,
CanvasRenderer
]);
/**
* Echarts hooks函数
* @param options - 图表配置
* @param renderFun - 图表渲染函数(例如:图表监听函数)
* @description 按需引入图表组件,没注册的组件需要先引入
*/
export function useEcharts(
options: Ref<ECOption> | ComputedRef<ECOption>,
renderFun?: (chartInstance: echarts.ECharts) => void
) {
const theme = useThemeStore();
const domRef = ref<HTMLElement>();
const initialSize = { width: 0, height: 0 };
const { width, height } = useElementSize(domRef, initialSize);
let chart: echarts.ECharts | null = null;
function canRender() {
return initialSize.width > 0 && initialSize.height > 0;
}
function isRendered() {
return Boolean(domRef.value && chart);
}
function update(updateOptions: ECOption) {
if (isRendered()) {
chart!.setOption({ ...updateOptions, backgroundColor: 'transparent' });
}
}
async function render() {
if (domRef.value) {
const chartTheme = theme.darkMode ? 'dark' : 'light';
await nextTick();
chart = echarts.init(domRef.value, chartTheme);
if (renderFun) {
renderFun(chart);
}
update(options.value);
}
}
function resize() {
chart?.resize();
}
function destroy() {
chart?.dispose();
}
function updateTheme() {
destroy();
render();
}
const stopSizeWatch = watch([width, height], ([newWidth, newHeight]) => {
initialSize.width = newWidth;
initialSize.height = newHeight;
if (newWidth === 0 && newHeight === 0) {
// 节点被删除 将chart置为空
chart = null;
}
if (canRender()) {
if (!isRendered()) {
render();
} else {
resize();
}
}
});
const stopOptionWatch = watch(
() => options.value,
newValue => {
update(newValue);
},
{ deep: true }
);
const stopDarkModeWatch = watch(
() => theme.darkMode,
() => {
updateTheme();
}
);
onUnmounted(() => {
destroy();
stopSizeWatch();
stopOptionWatch();
stopDarkModeWatch();
});
return {
domRef,
chart
};
}
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment