initHeader

This commit is contained in:
2024-08-22 11:27:06 +08:00
parent fe895bd37c
commit e0aaa7a30d
178 changed files with 5726 additions and 4999 deletions

11
frontend/.env Normal file
View File

@@ -0,0 +1,11 @@
# title
VITE_GLOB_APP_TITLE=自动检测平台
# 本地运行端口号
VITE_PORT=18091
# 启动时自动打开浏览器
VITE_OPEN=true
# 打包后是否生成包分析文件
VITE_REPORT=false

View File

@@ -1,6 +1,21 @@
NODE_ENV='development'
# 本地环境
VITE_USER_NODE_ENV=development
# 公共基础路径
VITE_PUBLIC_PATH=/
VITE_TITLE=""
# 路由模式
# Optional: hash | history
VITE_ROUTER_MODE=hash
# 打包时是否删除 console
VITE_DROP_CONSOLE=true
# 是否开启 VitePWA
VITE_PWA=false
# 开发环境接口地址
VITE_API_URL=/api
# 开发环境跨域代理,支持配置多个
VITE_PROXY=[["/api","http://192.168.1.125:18092/"]]

View File

@@ -1,6 +1,25 @@
NODE_ENV='production'
# 线上环境
VITE_USER_NODE_ENV=production
# 公共基础路径
VITE_PUBLIC_PATH=/
VITE_TITLE=""
# 路由模式
# Optional: hash | history
VITE_ROUTER_MODE=hash
# 是否启用 gzip 或 brotli 压缩打包,如果需要多个压缩规则,可以使用 “,” 分隔
# Optional: gzip | brotli | none
VITE_BUILD_COMPRESS=none
# 打包压缩后是否删除源文件
VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE=false
# 打包时是否删除 console
VITE_DROP_CONSOLE=true
# 是否开启 VitePWA
VITE_PWA=true
# 线上环境接口地址
VITE_API_URL="https://mock.mengxuegu.com/mock/629d727e6163854a32e8307e"

46
frontend/build/getEnv.ts Normal file
View File

@@ -0,0 +1,46 @@
import path from "path";
export function isDevFn(mode: string): boolean {
return mode === "development";
}
export function isProdFn(mode: string): boolean {
return mode === "production";
}
export function isTestFn(mode: string): boolean {
return mode === "test";
}
/**
* Whether to generate package preview
*/
export function isReportMode(): boolean {
return process.env.VITE_REPORT === "true";
}
// Read all environment variable configuration files to process.env
export function wrapperEnv(envConf: Recordable): ViteEnv {
const ret: any = {};
for (const envName of Object.keys(envConf)) {
let realName = envConf[envName].replace(/\\n/g, "\n");
realName = realName === "true" ? true : realName === "false" ? false : realName;
if (envName === "VITE_PORT") realName = Number(realName);
if (envName === "VITE_PROXY") {
try {
realName = JSON.parse(realName);
} catch (error) {}
}
ret[envName] = realName;
}
return ret;
}
/**
* Get user root directory
* @param dir file path
*/
export function getRootPath(...dir: string[]) {
return path.resolve(process.cwd(), ...dir);
}

108
frontend/build/plugins.ts Normal file
View File

@@ -0,0 +1,108 @@
import { resolve } from "path";
import { PluginOption } from "vite";
import { VitePWA } from "vite-plugin-pwa";
import { visualizer } from "rollup-plugin-visualizer";
import { createHtmlPlugin } from "vite-plugin-html";
import { createSvgIconsPlugin } from "vite-plugin-svg-icons";
import vue from "@vitejs/plugin-vue";
import vueJsx from "@vitejs/plugin-vue-jsx";
import eslintPlugin from "vite-plugin-eslint";
import viteCompression from "vite-plugin-compression";
import vueSetupExtend from "unplugin-vue-setup-extend-plus/vite";
/**
* 创建 vite 插件
* @param viteEnv
*/
export const createVitePlugins = (viteEnv: ViteEnv): (PluginOption | PluginOption[])[] => {
const { VITE_GLOB_APP_TITLE, VITE_REPORT, VITE_PWA } = viteEnv;
return [
vue(),
// vue 可以使用 jsx/tsx 语法
vueJsx(),
// esLint 报错信息显示在浏览器界面上
eslintPlugin(),
// name 可以写在 script 标签上
vueSetupExtend({}),
// 创建打包压缩配置
createCompression(viteEnv),
// 注入变量到 html 文件
createHtmlPlugin({
inject: {
data: { title: VITE_GLOB_APP_TITLE }
}
}),
// 使用 svg 图标
createSvgIconsPlugin({
iconDirs: [resolve(process.cwd(), "src/assets/icons")],
symbolId: "icon-[dir]-[name]"
}),
// vitePWA
VITE_PWA && createVitePwa(viteEnv),
// 是否生成包预览,分析依赖包大小做优化处理
VITE_REPORT && (visualizer({ filename: "stats.html", gzipSize: true, brotliSize: true }) as PluginOption)
];
};
/**
* @description 根据 compress 配置,生成不同的压缩规则
* @param viteEnv
*/
const createCompression = (viteEnv: ViteEnv): PluginOption | PluginOption[] => {
const { VITE_BUILD_COMPRESS = "none", VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE } = viteEnv;
const compressList = VITE_BUILD_COMPRESS.split(",");
const plugins: PluginOption[] = [];
if (compressList.includes("gzip")) {
plugins.push(
viteCompression({
ext: ".gz",
algorithm: "gzip",
deleteOriginFile: VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE
})
);
}
if (compressList.includes("brotli")) {
plugins.push(
viteCompression({
ext: ".br",
algorithm: "brotliCompress",
deleteOriginFile: VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE
})
);
}
return plugins;
};
/**
* @description VitePwa
* @param viteEnv
*/
const createVitePwa = (viteEnv: ViteEnv): PluginOption | PluginOption[] => {
const { VITE_GLOB_APP_TITLE } = viteEnv;
return VitePWA({
registerType: "autoUpdate",
manifest: {
name: VITE_GLOB_APP_TITLE,
short_name: VITE_GLOB_APP_TITLE,
theme_color: "#ffffff",
icons: [
{
src: "/logo.png",
sizes: "192x192",
type: "image/png"
},
{
src: "/logo.png",
sizes: "512x512",
type: "image/png"
},
{
src: "/logo.png",
sizes: "512x512",
type: "image/png",
purpose: "any maskable"
}
]
}
});
};

30
frontend/build/proxy.ts Normal file
View File

@@ -0,0 +1,30 @@
import type { ProxyOptions } from "vite";
type ProxyItem = [string, string];
type ProxyList = ProxyItem[];
type ProxyTargetList = Record<string, ProxyOptions>;
/**
* 创建代理,用于解析 .env.development 代理配置
* @param list
*/
export function createProxy(list: ProxyList = []) {
const ret: ProxyTargetList = {};
for (const [prefix, target] of list) {
const httpsRE = /^https:\/\//;
const isHttps = httpsRE.test(target);
// https://github.com/http-party/node-http-proxy#options
ret[prefix] = {
target: target,
changeOrigin: true,
ws: true,
rewrite: path => path.replace(new RegExp(`^${prefix}`), ""),
// https is require secure=false
...(isHttps ? { secure: false } : {})
};
}
return ret;
}

View File

@@ -100,6 +100,15 @@
</div>
</div>
<div id="app"></div>
<script>
const globalState = JSON.parse(window.localStorage.getItem("cn-global"));
if (globalState) {
const dot = document.querySelectorAll(".dot i");
const html = document.querySelector("html");
dot.forEach(item => (item.style.background = globalState.primary));
if (globalState.isDark) html.style.background = "#141414";
}
</script>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

View File

@@ -1,11 +1,11 @@
{
"name": "frontend",
"version": "0.0.0",
"version": "0.0.1",
"private": true,
"type": "module",
"scripts": {
"dev": "vite --host --port 18091",
"serve": "vite --host --port 18091",
"dev": "vite",
"serve": "vite",
"build-staging": "vite build --mode staging",
"build": "vite build",
"preview": "vite preview",
@@ -14,34 +14,69 @@
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.1",
"@vueuse/core": "^10.4.1",
"@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "^5.1.12",
"axios": "^1.7.3",
"crypto-js": "^4.2.0",
"dayjs": "^1.11.9",
"driver.js": "^1.3.0",
"echarts": "^5.4.3",
"echarts-liquidfill": "^3.1.0",
"element-plus": "^2.7.8",
"md5": "^2.3.0",
"mitt": "^3.0.1",
"nprogress": "^0.2.0",
"pinia": "^2.2.1",
"pinia-plugin-persistedstate": "^3.2.1",
"print-js": "^1.6.0",
"qs": "^6.11.2",
"screenfull": "^6.0.2",
"sortablejs": "^1.15.0",
"vue": "^3.4.29",
"vue-router": "^4.3.3"
"vue-i18n": "^9.4.0",
"vue-router": "^4.3.3",
"vuedraggable": "^4.1.0"
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.8.0",
"@tsconfig/node20": "^20.1.4",
"@types/md5": "^2.3.2",
"@types/node": "^20.14.14",
"@types/nprogress": "^0.2.0",
"@types/qs": "^6.9.8",
"@types/sortablejs": "^1.15.2",
"@typescript-eslint/eslint-plugin": "^6.7.0",
"@typescript-eslint/parser": "^6.7.0",
"@vitejs/plugin-vue": "^5.0.5",
"@vitejs/plugin-vue-jsx": "^3.0.2",
"@vue/eslint-config-typescript": "^13.0.0",
"@vue/tsconfig": "^0.5.1",
"autoprefixer": "^10.4.20",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0",
"eslint-plugin-vue": "^9.23.0",
"npm-run-all2": "^6.2.0",
"postcss": "^8.4.41",
"prettier": "^3.0.3",
"rollup-plugin-visualizer": "^5.9.2",
"sass": "^1.77.8",
"standard-version": "^9.5.0",
"tailwindcss": "^3.4.7",
"typescript": "~5.4.0",
"unplugin-auto-import": "^0.18.2",
"unplugin-vue-components": "^0.27.4",
"unplugin-vue-setup-extend-plus": "^1.0.0",
"vite": "^5.3.1",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-eslint": "^1.8.1",
"vite-plugin-html": "^3.2.0",
"vite-plugin-node-polyfills": "^0.22.0",
"vite-plugin-pwa": "^0.16.5",
"vite-plugin-svg-icons": "^2.0.1",
"vue-tsc": "^2.0.21"
},
"engines": {
"node": ">=16.0.0"
}
}

View File

@@ -1,6 +1,6 @@
<template>
<!--element-plus语言国际化全局修改为中文-->
<el-config-provider :locale='zhCn'>
<el-config-provider :locale='locale' :size="assemblySize" :button="buttonConfig">
<router-view />
</el-config-provider>
</template>
@@ -9,8 +9,42 @@
defineOptions({
name: 'App',
})
import { ElConfigProvider } from 'element-plus'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
import { onMounted, reactive, computed } from "vue";
import { useI18n } from "vue-i18n";
import { getBrowserLang } from "@/utils";
import { useTheme } from "@/hooks/useTheme";
import { ElConfigProvider } from "element-plus";
import { LanguageType } from "./stores/interface";
import { useGlobalStore } from "@/stores/modules/global";
import en from "element-plus/es/locale/lang/en";
import zhCn from "element-plus/es/locale/lang/zh-cn";
const globalStore = useGlobalStore();
// init theme
const { initTheme } = useTheme();
initTheme();
// init language
const i18n = useI18n();
onMounted(() => {
const language = globalStore.language ?? getBrowserLang();
i18n.locale.value = language;
globalStore.setGlobalState("language", language as LanguageType);
});
// element language
const locale = computed(() => {
if (globalStore.language == "zh") return zhCn;
if (globalStore.language == "en") return en;
return getBrowserLang() == "zh" ? zhCn : en;
});
// element assemblySize
const assemblySize = computed(() => globalStore.assemblySize);
// element button config
const buttonConfig = reactive({ autoInsertSpace: false });
document.getElementById('loadingPage')?.remove()

View File

@@ -0,0 +1,2 @@
// 后端微服务模块前缀
export const PORT1 = "/admin";

View File

@@ -0,0 +1,47 @@
// ? 暂未使用,目前使用全局 Loading 来控制重复请求
import { CustomAxiosRequestConfig } from "../index";
import qs from "qs";
// 声明一个 Map 用于存储每个请求的标识 和 取消函数
let pendingMap = new Map<string, AbortController>();
// 序列化参数
export const getPendingUrl = (config: CustomAxiosRequestConfig) =>
[config.method, config.url, qs.stringify(config.data), qs.stringify(config.params)].join("&");
export class AxiosCanceler {
/**
* @description: 添加请求
* @param {Object} config
* @return void
*/
addPending(config: CustomAxiosRequestConfig) {
// 在请求开始前,对之前的请求做检查取消操作
this.removePending(config);
const url = getPendingUrl(config);
const controller = new AbortController();
config.signal = controller.signal;
pendingMap.set(url, controller);
}
/**
* @description: 移除请求
* @param {Object} config
*/
removePending(config: CustomAxiosRequestConfig) {
const url = getPendingUrl(config);
// 如果在 pending 中存在当前请求标识,需要取消当前请求
const controller = pendingMap.get(url);
controller && controller.abort();
}
/**
* @description: 清空所有pending
*/
removeAllPending() {
pendingMap.forEach(controller => {
controller && controller.abort();
});
pendingMap.clear();
}
}

View File

@@ -0,0 +1,43 @@
import { ElMessage } from "element-plus";
/**
* @description: 校验网络请求状态码
* @param {Number} status
* @return void
*/
export const checkStatus = (status: number) => {
switch (status) {
case 400:
ElMessage.error("请求失败!请您稍后重试");
break;
case 401:
ElMessage.error("登录失效!请您重新登录");
break;
case 403:
ElMessage.error("当前账号无权限访问!");
break;
case 404:
ElMessage.error("你所访问的资源不存在!");
break;
case 405:
ElMessage.error("请求方式错误!请您稍后重试");
break;
case 408:
ElMessage.error("请求超时!请您稍后重试");
break;
case 500:
ElMessage.error("服务异常!");
break;
case 502:
ElMessage.error("网关错误!");
break;
case 503:
ElMessage.error("服务不可用!");
break;
case 504:
ElMessage.error("网关超时!");
break;
default:
ElMessage.error("请求失败!");
}
};

110
frontend/src/api/index.ts Normal file
View File

@@ -0,0 +1,110 @@
import axios, { AxiosInstance, AxiosError, AxiosRequestConfig, InternalAxiosRequestConfig, AxiosResponse } from "axios";
import { showFullScreenLoading, tryHideFullScreenLoading } from "@/components/Loading/fullScreen";
import { LOGIN_URL } from "@/config";
import { ElMessage } from "element-plus";
import { ResultData } from "@/api/interface";
import { ResultEnum } from "@/enums/httpEnum";
import { checkStatus } from "./helper/checkStatus";
import { useUserStore } from "@/stores/modules/user";
import router from "@/routers";
export interface CustomAxiosRequestConfig extends InternalAxiosRequestConfig {
loading?: boolean;
}
const config = {
// 默认地址请求地址,可在 .env.** 文件中修改
baseURL: import.meta.env.VITE_API_URL as string,
// 设置超时时间
timeout: ResultEnum.TIMEOUT as number,
// 跨域时候允许携带凭证
withCredentials: true
};
class RequestHttp {
service: AxiosInstance;
public constructor(config: AxiosRequestConfig) {
// instantiation
this.service = axios.create(config);
/**
* @description 请求拦截器
* 客户端发送请求 -> [请求拦截器] -> 服务器
* token校验(JWT) : 接受服务器返回的 token,存储到 vuex/pinia/本地储存当中
*/
this.service.interceptors.request.use(
(config: CustomAxiosRequestConfig) => {
const userStore = useUserStore();
// 当前请求不需要显示 loading在 api 服务中通过指定的第三个参数: { loading: false } 来控制
config.loading ?? (config.loading = true);
config.loading && showFullScreenLoading();
if (config.headers && typeof config.headers.set === "function") {
config.headers.set("x-access-token", userStore.token);
}
return config;
},
(error: AxiosError) => {
return Promise.reject(error);
}
);
/**
* @description 响应拦截器
* 服务器换返回信息 -> [拦截统一处理] -> 客户端JS获取到信息
*/
this.service.interceptors.response.use(
(response: AxiosResponse) => {
const { data } = response;
const userStore = useUserStore();
tryHideFullScreenLoading();
// 登陆失效
if (data.code == ResultEnum.OVERDUE) {
userStore.setToken("");
router.replace(LOGIN_URL);
ElMessage.error(data.message);
return Promise.reject(data);
}
// 全局错误信息拦截(防止下载文件的时候返回数据流,没有 code 直接报错)
if (data.code && data.code !== ResultEnum.SUCCESS) {
ElMessage.error(data.message);
return Promise.reject(data);
}
// 成功请求(在页面上除非特殊情况,否则不用处理失败逻辑)
return data;
},
async (error: AxiosError) => {
const { response } = error;
tryHideFullScreenLoading();
// 请求超时 && 网络错误单独判断,没有 response
if (error.message.indexOf("timeout") !== -1) ElMessage.error("请求超时!请您稍后重试");
if (error.message.indexOf("Network Error") !== -1) ElMessage.error("网络错误!请您稍后重试");
// 根据服务器响应的错误状态码,做不同的处理
if (response) checkStatus(response.status);
// 服务器结果都没有返回(可能服务器错误可能客户端断网),断网处理:可以跳转到断网页面
if (!window.navigator.onLine) router.replace("/500");
return Promise.reject(error);
}
);
}
/**
* @description 常用请求方法封装
*/
get<T>(url: string, params?: object, _object = {}): Promise<ResultData<T>> {
return this.service.get(url, { params, ..._object });
}
post<T>(url: string, params?: object | string, _object = {}): Promise<ResultData<T>> {
return this.service.post(url, params, _object);
}
put<T>(url: string, params?: object, _object = {}): Promise<ResultData<T>> {
return this.service.put(url, params, _object);
}
delete<T>(url: string, params?: any, _object = {}): Promise<ResultData<T>> {
return this.service.delete(url, { params, ..._object });
}
download(url: string, params?: object, _object = {}): Promise<BlobPart> {
return this.service.post(url, params, { ..._object, responseType: "blob" });
}
}
export default new RequestHttp(config);

View File

@@ -0,0 +1,90 @@
// 请求响应参数不包含data
export interface Result {
code: string;
message: string;
}
// 请求响应参数包含data
export interface ResultData<T = any> extends Result {
data: T;
}
// 分页响应参数
export interface ResPage<T> {
list: T[];
pageNum: number;
pageSize: number;
total: number;
}
// 分页请求参数
export interface ReqPage {
pageNum: number;
pageSize: number;
}
// 文件上传模块
export namespace Upload {
export interface ResFileUrl {
fileUrl: string;
}
}
// 登录模块
export namespace Login {
export interface ReqLoginForm {
username: string;
password: string;
}
export interface ResLogin {
accessToken: string;
}
export interface ResAuthButtons {
[key: string]: string[];
}
}
// 用户管理模块
export namespace User {
export interface ReqUserParams extends ReqPage {
username: string;
gender: number;
idCard: string;
email: string;
address: string;
createTime: string[];
status: number;
}
export interface ResUserList {
id: string;
username: string;
gender: number;
user: { detail: { age: number } };
idCard: string;
email: string;
address: string;
createTime: string;
status: number;
avatar: string;
photo: any[];
children?: ResUserList[];
}
export interface ResStatus {
userLabel: string;
userValue: number;
}
export interface ResGender {
genderLabel: string;
genderValue: number;
}
export interface ResDepartment {
id: string;
name: string;
children?: ResDepartment[];
}
export interface ResRole {
id: string;
name: string;
children?: ResDepartment[];
}
}

View File

@@ -1,9 +0,0 @@
/**
* 主进程与渲染进程通信频道定义
* Definition of communication channels between main process and rendering process
*/
const ipcApiRoute = {
test: 'controller.example.test',
}
export { ipcApiRoute }

View File

@@ -0,0 +1,26 @@
import { Login } from "@/api/interface/index";
import { PORT1 } from "@/api/config/servicePort";
import http from "@/api";
/**
* @name 登录模块
*/
// 用户登录
export const loginApi = (params: Login.ReqLoginForm) => {
return http.post<Login.ResLogin>(PORT1 + `/login`, params, { loading: false }); // 正常 post json 请求 ==> application/json
};
// 获取菜单列表
export const getAuthMenuListApi = () => {
return http.get<Menu.MenuOptions[]>(PORT1 + `/menu/list`, {}, { loading: false });
};
// 获取按钮权限
export const getAuthButtonListApi = () => {
return http.get<Login.ResAuthButtons>(PORT1 + `/auth/buttons`, {}, { loading: false });
};
// 用户退出登录
export const logoutApi = () => {
return http.post(PORT1 + `/logout`);
};

View File

@@ -0,0 +1,16 @@
import { Upload } from "@/api/interface/index";
import { PORT1 } from "@/api/config/servicePort";
import http from "@/api";
/**
* @name 文件上传模块
*/
// 图片上传
export const uploadImg = (params: FormData) => {
return http.post<Upload.ResFileUrl>(PORT1 + `/file/upload/img`, params);
};
// 视频上传
export const uploadVideo = (params: FormData) => {
return http.post<Upload.ResFileUrl>(PORT1 + `/file/upload/video`, params);
};

View File

@@ -0,0 +1,71 @@
import { ResPage, User } from "@/api/interface/index";
import { PORT1 } from "@/api/config/servicePort";
import http from "@/api";
/**
* @name 用户管理模块
*/
// 获取用户列表
export const getUserList = (params: User.ReqUserParams) => {
return http.post<ResPage<User.ResUserList>>(PORT1 + `/user/list`, params);
};
// 获取树形用户列表
export const getUserTreeList = (params: User.ReqUserParams) => {
return http.post<ResPage<User.ResUserList>>(PORT1 + `/user/tree/list`, params);
};
// 新增用户
export const addUser = (params: { id: string }) => {
return http.post(PORT1 + `/user/add`, params);
};
// 批量添加用户
export const BatchAddUser = (params: FormData) => {
return http.post(PORT1 + `/user/import`, params);
};
// 编辑用户
export const editUser = (params: { id: string }) => {
return http.post(PORT1 + `/user/edit`, params);
};
// 删除用户
export const deleteUser = (params: { id: string[] }) => {
return http.post(PORT1 + `/user/delete`, params);
};
// 切换用户状态
export const changeUserStatus = (params: { id: string; status: number }) => {
return http.post(PORT1 + `/user/change`, params);
};
// 重置用户密码
export const resetUserPassWord = (params: { id: string }) => {
return http.post(PORT1 + `/user/rest_password`, params);
};
// 导出用户数据
export const exportUserInfo = (params: User.ReqUserParams) => {
return http.download(PORT1 + `/user/export`, params);
};
// 获取用户状态字典
export const getUserStatus = () => {
return http.get<User.ResStatus[]>(PORT1 + `/user/status`);
};
// 获取用户性别字典
export const getUserGender = () => {
return http.get<User.ResGender[]>(PORT1 + `/user/gender`);
};
// 获取用户部门列表
export const getUserDepartment = () => {
return http.get<User.ResDepartment[]>(PORT1 + `/user/department`);
};
// 获取用户角色字典
export const getUserRole = () => {
return http.get<User.ResRole[]>(PORT1 + `/user/role`);
};

View File

@@ -1,57 +0,0 @@
import createAxios from '@/utils/http'
import { useUserInfoStore } from '@/stores/modules/user'
import { sm3Digest } from '@/assets/commjs/sm3.js'
import { sm2, encrypt } from '@/assets/commjs/sm2.js'
// 获取公钥
export const publicKey = (params?: any) => {
if (!params) {
const userInfo = useUserInfoStore()
params = {
loginName: encrypt(userInfo.loginName),
}
}
return createAxios({
url: '/user/generateSm2Key',
method: 'get',
params,
})
}
export const pwdSm3 = async (pwd: any, loginName?: string) => {
let publicKeyStr = await publicKey(
loginName ? { loginName: encrypt(loginName) } : false,
)
let sm3Pwd = sm3Digest(pwd) //SM3加密
return sm2(sm3Pwd + '|' + pwd, publicKeyStr.data, 0)
}
//登录获取token
export const login = async (params: any) => {
params.password = await pwdSm3(params.password, params.username)
params.username = encrypt(params.username)
return createAxios({
url: '/pqs-auth/oauth/token',
method: 'post',
params,
})
}
//获取用户信息
export const getUserById = () => {
const userInfo = useUserInfoStore()
return createAxios({
url: '/user-boot/user/getUserById?id=' + userInfo.userIndex,
method: 'get',
})
}
// 刷新token
export function refreshToken(): Promise<any> {
const userInfo = useUserInfoStore()
return login({
grant_type: 'refresh_token',
refresh_token: userInfo.refresh_token,
username: userInfo.loginName,
})
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,257 +0,0 @@
/**
* 国密摘要算法SM3
* @param str:raw string
* @return the 256-bit hex string produced by SM3 from a raw string
*/
function sm3Digest(str) {
//1. 转换为二进制数组
var binArr = str2bin(str2rstr_utf8(str));
//2. 填充
var groupNum = alignSM3(binArr, str.length);
//3. 迭代压缩
var v = new Array(8);//初始值
v[0] = 0x7380166f;
v[1] = 0x4914b2b9;
v[2] = 0x172442d7;
v[3] = 0xda8a0600;
v[4] = 0xa96f30bc;
v[5] = 0x163138aa;
v[6] = 0xe38dee4d;
v[7] = 0xb0fb0e4e;
//按 512bit 分组进行压缩
for (var i = 0; i < groupNum; i++) {
v = compress(v, binArr, i);
}
return word2str(v, '');
}
/**
* 将数组转换为字符串。数组长度不定,每个元素为 32bit 的数字。
* @param words:数组,每个元素为 32bit 的数字
* @param seperator:在每个数组元素转换得到的字符串之间的分隔符
*/
function word2str(words, seperator) {
var prefix = Array(8).join('0');
for (var i = 0; i < words.length; i++) {
//若 hex 不足 8 位,则高位补 0
words[i] = (prefix + (words[i] >>> 0).toString(16)).slice(-8);
}
return words.join(seperator);
}
/**
* 将字符串转换为二进制数组,默认字符串编码为 UTF-8且范围在 0x00~0xFF 内。
* 若某些字符的编码超过此范围,则会只保留最低字节。加密可正常进行,但加密结果有误。
* 每个数组元素包含 4 个字符,即 32 bit。
* @param 字符串
* @return 数组,长度为(字符串长度 / 4每个元素为 32bit 的数字
*/
function str2bin(str) {
var binary = new Array(str.length >> 2);
for (var i = 0; i < str.length * 8; i += 8) {
binary[i >> 5] |= (str.charCodeAt(i / 8) & 0xFF) << (24 - i % 32);
}
return binary;
}
/**
* 对明文的二进制串进行填充
* <pre>
* | 满足 mod 512 = 448 | 固定 64 位 |
* | 明文二进制 |填充部分|明文二进制串的长度的二进制表示|
* xxxxxxxxxxxx 10.....0 0...........................xx
* </pre>
* @param arr:数组,每个元素为 32bit 的数字
* @param strLen明文字符串长度
* @return 数组,每个元素为 32bit 的数字,数组长度为 16 的倍数(包括 16
*/
function alignSM3(arr, strLen) {
//在明文二进制串后面拼接 1000 0000
arr[strLen >> 2] |= 0x80 << (24 - strLen % 4 * 8);
var groupNum = ((strLen + 8) >> 6) + 1;//以 512bit 为一组,总的组数
var wordNum = groupNum * 16;//一个 word 32bit总的 word 数
for (var i = (strLen >> 2) + 1; i < wordNum; i++) {
arr[i] = 0;
}
arr[wordNum - 1] = strLen * 8;//在末尾填上明文的二进制长度
return groupNum;
}
/**
* 压缩函数中的置换函数
*/
function p0(x) {
return x ^ bitRol(x, 9) ^ bitRol(x, 17);
}
/**
* 压缩函数中的置换函数
*/
function p1(x) {
return x ^ bitRol(x, 15) ^ bitRol(x, 23);
}
/**
* 循环左移
*/
function bitRol(input, n) {
return (input << n) | (input >>> (32 - n));
}
/**
* 压缩函数
*/
function compress(v, binArr, i) {
//将消息分组扩展成 132 个字
var w1 = new Array(68);
var w2 = new Array(64);
for (var j = 0; j < 68; j++) {
if (j < 16) {
w1[j] = binArr[i * 16 + j];
} else {
w1[j] = p1(w1[j-16] ^ w1[j-9] ^ bitRol(w1[j-3], 15)) ^ bitRol(w1[j-13], 7) ^ w1[j-6];
}
}
for (var j = 0; j < 64; j++) {
w2[j] = w1[j] ^ w1[j+4];
}
//压缩
var a = v[0];
var b = v[1];
var c = v[2];
var d = v[3];
var e = v[4];
var f = v[5];
var g = v[6];
var h = v[7];
var ss1;
var ss2;
var tt1;
var tt2;
for (var j = 0; j < 64; j++) {
ss1 = bitRol(addAll(bitRol(a, 12) , e , bitRol(t(j), j)), 7);
ss2 = ss1 ^ bitRol(a, 12);
tt1 = addAll(ff(a, b, c, j) , d , ss2 , w2[j]);
tt2 = addAll(gg(e, f, g, j) , h , ss1 , w1[j]);
d = c;
c = bitRol(b, 9);
b = a;
a = tt1;
h = g;
g = bitRol(f, 19);
f = e;
e = p0(tt2);
}
v[0] ^= a;
v[1] ^= b;
v[2] ^= c;
v[3] ^= d;
v[4] ^= e;
v[5] ^= f;
v[6] ^= g;
v[7] ^= h;
return v;
}
/**
* 常量 T 随 j 的不同而不同
*/
function t(j) {
if (0 <= j && j < 16) {
return 0x79CC4519;
} else if (j < 64) {
return 0x7A879D8A;
}
}
/**
* 布尔函数,随 j 的变化取不同的表达式
*/
function ff(x, y, z, j) {
if (0 <= j && j < 16) {
return x ^ y ^ z;
} else if (j < 64) {
return (x & y) | (x & z) | (y & z);
}
}
/**
* 布尔函数,随 j 的变化取不同的表达式
*/
function gg(x, y, z, j) {
if (0 <= j && j < 16) {
return x ^ y ^ z;
} else if (j < 64) {
return (x & y) | (~x & z);
}
}
/**
* 两数相加
* 避免某些 js 引擎的 32 位加法的 bug
*/
function safe_add(x, y) {
var lsw = ( x & 0xFFFF ) + (y & 0xFFFF);
var msw = ( x >> 16 ) + (y >> 16) + (lsw >> 16);
return (msw << 16) | ( lsw & 0xFFFF );
}
/**
* 将所有参数相加
*/
function addAll() {
var sum = 0;
for (var i = 0; i < arguments.length; i++) {
sum = safe_add(sum, arguments[i]);
}
return sum;
}
/**
* UTF-16 --> UTF-8
*/
function str2rstr_utf8(input) {
var output = "" ;
var i = -1 ;
var x, y ;
while(++ i < input.length) {
//按 UTF-16 解码
x = input.charCodeAt(i);
y = i + 1 < input.length ? input .charCodeAt (i + 1) : 0 ;
if( 0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF ) {
x = 0x10000 + ((x & 0x03FF) << 10 ) + (y & 0x03FF);
i++;
}
//按 UTF-8 编码
if( x <= 0x7F ) {
output += String.fromCharCode(x);
}
else if(x <= 0x7FF) {
output += String.fromCharCode(
0xC0 | ((x >>> 6 ) & 0x1F),
0x80 | ( x & 0x3F ));
} else if(x <= 0xFFFF) {
output += String.fromCharCode(
0xE0 | ((x >>> 12) & 0x0F ),
0x80 | ((x >>> 6 ) & 0x3F),
0x80 | ( x & 0x3F ));
} else if(x <= 0x1FFFFF) {
output += String.fromCharCode(
0xF0 | ((x >>> 18) & 0x07 ),
0x80 | ((x >>> 12) & 0x3F),
0x80 | ((x >>> 6 ) & 0x3F),
0x80 | ( x & 0x3F ));
}
}
return output;
}
export {
sm3Digest
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,14 @@
@font-face {
font-family: YouSheBiaoTiHei;
src: url("./YouSheBiaoTiHei.ttf");
}
@font-face {
font-family: MetroDF;
src: url("./MetroDF.ttf");
}
@font-face {
font-family: DIN;
src: url("./DIN.Otf");
}

View File

@@ -0,0 +1,48 @@
@font-face {
font-family: iconfont; /* Project id 2667653 */
src: url("iconfont.ttf?t=1694681005434") format("truetype");
}
.iconfont {
font-family: iconfont !important;
font-size: 20px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
cursor: pointer;
}
.icon-yiwen::before {
font-size: 15px;
content: "\e693";
}
.icon-xiala::before {
content: "\e62b";
}
.icon-tuichu::before {
content: "\e645";
}
.icon-xiaoxi::before {
font-size: 21.2px;
content: "\e61f";
}
.icon-zhuti::before {
font-size: 22.4px;
content: "\e638";
}
.icon-sousuo::before {
content: "\e611";
}
.icon-contentright::before {
content: "\e8c9";
}
.icon-contentleft::before {
content: "\e8ca";
}
.icon-fangda::before {
content: "\e826";
}
.icon-suoxiao::before {
content: "\e641";
}
.icon-zhongyingwen::before {
content: "\e8cb";
}

Binary file not shown.

View File

@@ -1 +0,0 @@
<svg t="1723085514271" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1125" width="200" height="200"><path d="M765.22 393.6H184.15c-8.28 0-15-6.72-15-15s6.72-15 15-15h581.07c37.38 0 67.8-30.41 67.8-67.8s-30.41-67.8-67.8-67.8-67.8 30.41-67.8 67.8c0 8.28-6.72 15-15 15s-15-6.72-15-15c0-53.93 43.87-97.8 97.8-97.8s97.8 43.87 97.8 97.8-43.87 97.8-97.8 97.8zM783.99 809.79c-43.58 0-79.03-35.45-79.03-79.03 0-8.28 6.72-15 15-15s15 6.72 15 15c0 27.04 22 49.03 49.03 49.03s49.03-22 49.03-49.03-22-49.03-49.03-49.03H297.67c-8.28 0-15-6.72-15-15s6.72-15 15-15h486.32c43.58 0 79.03 35.45 79.03 79.03s-35.45 79.03-79.03 79.03z" fill="#333333" p-id="1126"></path><path d="M481.66 306.63H312.79c-8.28 0-15-6.72-15-15s6.72-15 15-15h168.87c8.28 0 15 6.72 15 15s-6.72 15-15 15zM227.46 681.49h-43c-8.28 0-15-6.72-15-15s6.72-15 15-15h43c8.28 0 15 6.72 15 15s-6.72 15-15 15zM777.81 598.8h-43c-8.28 0-15-6.72-15-15s6.72-15 15-15h43c8.28 0 15 6.72 15 15s-6.72 15-15 15zM383.78 761H241.87c-8.28 0-15-6.72-15-15s6.72-15 15-15h141.91c8.28 0 15 6.72 15 15s-6.72 15-15 15zM848.02 452.99H613.98c-8.28 0-15-6.72-15-15s6.72-15 15-15h234.04c8.28 0 15 6.72 15 15s-6.72 15-15 15z" fill="#333333" p-id="1127"></path><path d="M492 535.33m-243.33 0a243.33 243.33 0 1 0 486.66 0 243.33 243.33 0 1 0-486.66 0Z" fill="#FFFFFF" p-id="1128"></path><path d="M492 793.67c-69 0-133.88-26.87-182.67-75.66-48.79-48.79-75.66-113.67-75.66-182.67s26.87-133.88 75.66-182.67C358.12 303.88 423 277.01 492 277.01s133.88 26.87 182.67 75.66c48.79 48.79 75.66 113.67 75.66 182.67s-26.87 133.88-75.66 182.67C625.88 766.8 561 793.67 492 793.67zM492 307c-60.99 0-118.33 23.75-161.46 66.88-43.13 43.13-66.88 100.47-66.88 161.46s23.75 118.33 66.88 161.46c43.13 43.13 100.47 66.88 161.46 66.88s118.33-23.75 161.46-66.88 66.88-100.47 66.88-161.46-23.75-118.33-66.88-161.46S552.99 307 492 307z" fill="#333333" p-id="1129"></path><path d="M583.85 517.56l-58.52-11.99 24.71-97.1c5.39-21.17-20.94-35.62-35.9-19.71L393.3 517.17c-11.35 12.06-5.06 31.89 11.16 35.22l58.52 11.99-24.71 97.1c-5.39 21.17 20.94 35.62 35.9 19.71l120.84-128.41c11.35-12.06 5.06-31.89-11.16-35.22z" fill="#52E217" p-id="1130"></path><path d="M458.92 702.95c-5.94 0-11.96-1.5-17.55-4.57-14.68-8.06-21.77-24.38-17.64-40.6l20.83-81.86-43.12-8.83c-13.02-2.67-23.19-11.82-27.21-24.49s-0.97-26.02 8.13-35.7L503.2 378.49c11.48-12.2 29.05-14.97 43.73-6.92 14.68 8.06 21.77 24.38 17.64 40.6l-20.83 81.86 43.12 8.83c13.02 2.67 23.19 11.82 27.21 24.49s0.97 26.02-8.13 35.7L485.1 691.46c-7.1 7.54-16.53 11.48-26.17 11.49z m70.43-305.96c-1.33 0-2.84 0.5-4.3 2.04L404.21 527.44c-2.15 2.29-1.79 4.79-1.38 6.07 0.41 1.29 1.55 3.54 4.63 4.17l58.52 11.99c4.02 0.82 7.52 3.25 9.7 6.73 2.18 3.48 2.84 7.69 1.83 11.66l-24.71 97.1c-1.02 4.02 1.44 6.05 3 6.91 1.56 0.86 4.6 1.84 7.44-1.18l120.84-128.41c2.15-2.29 1.79-4.79 1.38-6.07-0.41-1.29-1.55-3.54-4.63-4.17l-58.52-11.99c-4.02-0.82-7.52-3.25-9.7-6.73a14.993 14.993 0 0 1-1.83-11.66l24.71-97.1c1.02-4.02-1.44-6.05-3-6.91-0.76-0.42-1.87-0.87-3.14-0.87z" fill="#333333" p-id="1131"></path></svg>

Before

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -0,0 +1 @@
<?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 class="icon" width="200px" height="200.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M574 342.8m-38.3 0a38.3 38.3 0 1 0 76.6 0 38.3 38.3 0 1 0-76.6 0Z" fill="#F2B843" /><path d="M627 697c15.2-20.7 45.3-294-45-383.3-3-6.1-0.4-13.5 5.7-16.5 6.2-3 13.5-0.5 16.5 5.7C689.8 370.2 719.9 573 705.1 697H627z" fill="#EA800C" /><path d="M617.8 307.4m-38.3 0a38.3 38.3 0 1 0 76.6 0 38.3 38.3 0 1 0-76.6 0Z" fill="#F2B843" /><path d="M608 272.5L461 502.8c-33.6-47.5-37.2-112.5-3.9-164.6 33.2-52.1 93.7-76.2 150.9-65.7z" fill="#3DC38A" /><path d="M742.5 132.3L569.9 299.8c-19.2-47.5-9.1-103.9 29.9-141.8 39.1-37.9 95.8-46.3 142.7-25.7z" fill="#2F9B77" /><path d="M608.7 289.2l-239.6 21.1c15.1-49 58.5-86.4 112.7-91.1 54.2-4.8 103.5 24.4 126.9 70z" fill="#2F9B77" /><path d="M594.7 269.9L408.5 168.4c35-28.6 85.2-34.8 127.3-11.9 42.1 23 64 68.6 58.9 113.4z" fill="#3DC38A" /><path d="M825.5 331.8l-271.4-31.4c28-51 84.9-82.7 146.3-75.6 61.3 7 109.5 51 125.1 107z" fill="#3DC38A" /><path d="M75.3 868.9c0-86.5 104.3-173 233-173s233 86.5 233 173h-466z" fill="#2F9B77" /><path d="M938.2 868.9c0-116.2-130.9-232.3-292.3-232.3S353.5 752.7 353.5 868.9h584.7z" fill="#3DC38A" /><path d="M858.9 701.5c-28.1-23.1-60.4-41.4-95.9-54.3-14.5-5.3-29.3-9.5-44.3-12.8 0.2-51.3-5.5-106.3-16.2-155.9-11.9-54.8-29.5-101.6-51.2-136.3 5.6-5.3 9.7-11.8 12.2-19.1l160.8 18.6c0.4 0 0.8 0.1 1.2 0.1 2.9 0 5.7-1.3 7.6-3.5 2.2-2.5 2.9-6 2-9.2-8.3-29.8-25.1-56.3-48.5-76.7-24-20.9-53.5-33.9-85.1-37.5-9.7-1.1-19.3-1.3-28.9-0.7l76.8-74.6c2.4-2.3 3.5-5.7 2.9-8.9-0.6-3.3-2.8-6-5.8-7.4-52.3-23-112.6-12.1-153.7 27.7-7.2 7-13.6 14.7-19.1 23.1-9.4-10.5-20.6-19.4-33.1-26.2-44.7-24.3-99-19.2-138.4 12.9-2.6 2.1-3.9 5.4-3.6 8.7s2.2 6.3 5.2 7.9l62.5 34c-50.2 9.8-91.2 46.2-106.6 96-1 3.2-0.3 6.6 1.8 9.2 1.9 2.4 4.8 3.7 7.8 3.7h0.9l94.5-8.3c-5.8 6.4-11 13.3-15.8 20.8-17.2 26.9-25.7 57.9-24.7 89.7 1 31 11 60.8 28.9 86 1.9 2.7 4.9 4.2 8.2 4.2h0.2c3.3-0.1 6.4-1.8 8.2-4.6L549 383.9c7.5 4.6 16.1 7.1 25.2 7.1 13.4 0 25.9-5.5 34.8-14.7 27.2 70.9 29.2 175.3 21.8 250.6-34.9 1.5-69.1 8.3-101.8 20.2-35.5 12.9-67.8 31.2-95.9 54.3-3.2 2.6-6.3 5.3-9.4 8.1-35.7-15.5-75.4-23.6-115.1-23.6-63.1 0-123.8 19.9-170.9 56.1-45.8 35.3-72.1 81.5-72.1 126.9 0 5.5 4.5 10 10 10h862.9c5.5 0 10-4.5 10-10-0.3-59.7-32.8-120.7-89.6-167.4z m-226.2-370c-3.3 2.1-7 3.4-10.9 3.9-1-6.4-3.2-12.5-6.5-17.9l27.6 3.2c-2.3 4.4-5.8 8.1-10.2 10.8z m66.6-96.8c27.6 3.2 53.3 14.5 74.3 32.7 16.6 14.5 29.4 32.4 37.5 52.6l-152.7-17.7c-0.4-0.1-0.8-0.2-1.2-0.2-0.4 0-0.8-0.1-1.2-0.1l-65.3-7.6c-1-0.3-2-0.4-2.9-0.3l-5.5-0.6c-0.1 0-0.2-0.1-0.3-0.1-0.7-0.1-1.3-0.2-2-0.2l-8.8-1c5.3-7.5 11.3-14.5 18-20.8 0.5-0.4 1-0.8 1.4-1.3 8.7-8 18.4-14.9 29.1-20.5 8-4.2 16.4-7.6 24.9-10.2 0.5-0.1 0.9-0.2 1.4-0.4 17-4.8 35.1-6.4 53.3-4.3z m-92.5-69.4c31.5-30.5 76.2-41.2 117.4-29l-87 84.4c-9.3 2.9-18.4 6.6-27.1 11.2-2.2 1.2-4.3 2.4-6.5 3.6-2.8-15.7-8.5-30.8-17-44.4 5.5-9.5 12.3-18.2 20.2-25.8z m-75.8 0.1c14.4 7.9 26.4 18.6 35.7 31.9 10.5 15.1 16.8 32.7 18.4 51-1.2 1-2.5 2-3.6 3l-74-40.3c-0.8-0.7-1.8-1.2-2.8-1.5l-77.2-42.1c31.3-18.8 70.6-20 103.5-2z m-48.2 63.8c5.2-0.5 10.3-0.6 15.4-0.4l68.2 37.1c-5.1 5.7-9.9 11.8-14.2 18.2l-60 5.3-108.1 9.5c17.8-39.1 55-66 98.7-69.7zM461.2 484c-24.3-43.9-23-97.5 4.5-140.6 8.5-13.4 19-24.9 31.3-34.4l48.3-4.2s0 0.1 0.1 0.1c1.5 3 4.4 5 7.7 5.3l17.8 2.1-32.1 50.3c-0.6 0.7-1 1.4-1.4 2.2L461.2 484zM574 371c-5.2 0-10.1-1.4-14.4-3.9l29.3-45.9 1.1-1.8c7.6 5.2 12.4 13.9 12.4 23.4v2.1c-0.1 1.8-0.5 3.8-1.1 6.1-0.2 0.6-0.4 1.1-0.6 1.7-4.2 10.9-14.8 18.3-26.7 18.3z m47.8-15.5c4.3-0.3 8.6-1.3 12.6-2.7 20.5 32.6 37.2 77.3 48.6 129.9 10.2 47.1 15.7 99.1 15.8 148-15.9-2.5-31.9-3.8-48.1-4 2.7-28.8 5.6-76.5 1.8-130.4-4-57.6-14.3-104.8-30.7-140.8zM149.6 757.9c43.6-33.5 99.9-52 158.7-52 34.2 0 68.3 6.5 99.4 18.8-38.4 40-61.1 87.2-63.9 134.2h-258c3.6-35.9 26.4-72.2 63.8-101z m391.7 101H363.8c3.4-50.5 32.8-101.8 81.7-142 26.4-21.7 56.6-38.8 90-50.9 35.4-12.8 72.5-19.4 110.4-19.4 37.9 0 75 6.5 110.3 19.4 33.4 12.1 63.6 29.3 90 50.9 48.9 40.2 78.2 91.5 81.6 142H541.3z" fill="#4D3500" /></svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 10 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

@@ -0,0 +1 @@
<?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 class="icon" width="200px" height="200.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M489.6 541.1V166l283.5 375.1H489.6" fill="#3DC38A" /><path d="M489.6 101.3l-323.2 491h323.2z" fill="#F2B843" /><path d="M489.6 715.7c-16.3 0-29.6-13.2-29.6-29.6V95c0-16.3 13.2-29.6 29.6-29.6 16.3 0 29.6 13.2 29.6 29.6v591.1c-0.1 16.3-13.3 29.6-29.6 29.6z" fill="#EA800C" /><path d="M489.6 608.4H145.3c-16.3 0-29.6-13.2-29.6-29.6 0-16.3 13.2-29.6 29.6-29.6h344.3c16.3 0 29.6 13.2 29.6 29.6-0.1 16.3-13.3 29.6-29.6 29.6z" fill="#EA800C" /><path d="M783.8 557.2H503.1c-16.3 0-29.6-13.2-29.6-29.6 0-16.3 13.2-29.6 29.6-29.6h280.7c16.3 0 29.6 13.2 29.6 29.6 0 16.3-13.3 29.6-29.6 29.6z" fill="#EA800C" /><path d="M752.4 759.8l-67-88.8c-7.9-10.5-20.3-16.7-33.5-16.7H302c-18.3 0-34.5 11.9-40 29.3l-25 76.2h515.4z" fill="#2F9B77" /><path d="M920.8 704.6c15.6-0.8 26.8 14.8 21.1 29.3l-54.5 138.8-28.6 72.8H133.7L81.5 775c-4.1-13.4 5.5-27 19.4-27.8l143.3-7.5 676.6-35.1z" fill="#3DC38A" /><path d="M802.3 791.8m-27 0a27 27 0 1 0 54 0 27 27 0 1 0-54 0Z" fill="#F2B843" /><path d="M947.5 707.7c-6.3-8.7-16.4-13.6-27.2-13.1l-196.8 10.2-30-39.9c-9.8-12.9-25.3-20.6-41.5-20.6H529.2v-77.1h254.7c21.8 0 39.6-17.8 39.6-39.6S805.7 488 783.9 488h-38.3L529.2 201.7V95c0-21.8-17.8-39.6-39.6-39.6S450 73.2 450 95v48.2L189.3 539.3h-44c-21.8 0-39.6 17.8-39.6 39.6s17.8 39.6 39.6 39.6H450v25.8H302c-22.7 0-42.6 14.6-49.5 36.2l-16.2 49.6-135.9 7.1c-9.7 0.6-18.5 5.5-24.1 13.5-5.6 8-7.1 17.9-4.3 27.2l52.2 170.5c1.3 4.2 5.2 7.1 9.6 7.1h725.1c4.1 0 7.8-2.5 9.3-6.3l28.6-72.8 54.5-138.8c3.8-10 2.4-21.2-3.8-29.9zM720.5 488H529.2V234.9L720.5 488zM450 179.6v359.7H213.3L450 179.6z m20 428.8c0-5.5-4.5-10-10-10-0.5 0-0.9 0-1.3 0.1H145.3c-10.8 0-19.6-8.8-19.6-19.6s8.8-19.6 19.6-19.6H460c5.5 0 10-4.5 10-10V95c0-10.8 8.8-19.6 19.6-19.6s19.6 8.8 19.6 19.6v403c0 5.5 4.5 10 10 10h264.7c10.8 0 19.6 8.8 19.6 19.6s-8.8 19.6-19.6 19.6H519.2c-5.5 0-10 4.5-10 10v87.1H470v-35.9z m-198.5 78.3s0-0.1 0 0c4.3-13.4 16.5-22.4 30.5-22.4h350c9.9 0 19.5 4.8 25.5 12.7l21.9 29.1L257.7 729l13.8-42.3z m661.1 43.5L878.1 869 852 935.5H141.1l-50-163.4c-1-3.4-0.5-7 1.6-9.9 2-2.9 5.3-4.8 8.8-5l141.5-7.4h0.6c0.6 0 1.1-0.1 1.7-0.1l473.6-24.6h0.7l201.7-10.5c4-0.2 7.6 1.5 9.9 4.8 2.3 3.2 2.8 7.2 1.4 10.8z" fill="#4D3500" /><path d="M802.3 754.8c-20.4 0-37 16.6-37 37s16.6 37 37 37 37-16.6 37-37-16.6-37-37-37z m0 54c-9.4 0-17-7.6-17-17s7.6-17 17-17 17 7.6 17 17-7.6 17-17 17z" fill="#4D3500" /></svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@@ -0,0 +1 @@
<?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 class="icon" width="200px" height="200.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M252.8 862.9h-73.4c-6.8 0-12.4-5.6-12.4-12.4V134.4c0-6.8 5.6-12.4 12.4-12.4h73.4l5.3 6.8v728.8l-5.3 5.3z" fill="#F2B843" /><path d="M338.5 942l-42.9-18.1-42.8 18.1v-79.1l7.5-5.3h71.3l7 5.3-0.1 79.1z" fill="#EA800C" /><path d="M844.6 327.9h-40.7l-5.3-5.8v-93.3l5.3-7.2h40.7c6.8 0 12.4 5.6 12.4 12.4v81.5c0 6.9-5.6 12.4-12.4 12.4z" fill="#F2B843" /><path d="M844.6 540.5h-40.7l-5.3-7.3v-90.6l5.3-8.4h40.7c6.8 0 12.4 5.6 12.4 12.4v81.5c0 6.8-5.6 12.4-12.4 12.4z" fill="#EA800C" /><path d="M844.6 753h-40.7l-5.3-6.7v-92.1l5.3-7.5h40.7c6.8 0 12.4 5.6 12.4 12.4v81.5c0 6.8-5.6 12.4-12.4 12.4z" fill="#2F9B77" /><path d="M791.8 862.9h-539V122h539.1c6.7 0 12 5.4 12 12v716.8c0 6.7-5.4 12.1-12.1 12.1z" fill="#3DC38A" /><path d="M680.3 661.1H343.7c-14 0-25.5-11.5-25.5-25.5s11.5-25.5 25.5-25.5h336.6c14 0 25.5 11.5 25.5 25.5s-11.5 25.5-25.5 25.5zM680.3 779.8H343.7c-14 0-25.5-11.5-25.5-25.5s11.5-25.5 25.5-25.5h336.6c14 0 25.5 11.5 25.5 25.5s-11.5 25.5-25.5 25.5z" fill="#2F9B77" /><path d="M594.8 511.1c-79.2 45.7-180.5 18.6-226.3-60.6S350 270 429.2 224.2s180.5-18.6 226.3 60.6 18.5 180.6-60.7 226.3z" fill="#3DC38A" /><path d="M523.7 318.1c-1.2 0.3-3.5 0.9-4.5 1.7-1.2 1 0.7 2.3 0 2.9-1.2 1-2.1 2.7-4.7 2.6-5.5-0.3-13.9-7.5-19-10.2 8.1-8.3-4.7-9.6-9.7-9.1-6.5 0.5-17.7 0-23.5 3.2-4.1 2.3-9.5 11.7-12.8 15.5-4.6 5.2-9.1 9.8-12.1 16-10.9 23 5.9 49.4 32.2 46.3 7.3-0.9 14.9-5.5 21.4 0.6 4.8 4.5 2.3 8.7 3.5 13.9 1.1 4.5 6.1 7.3 7.8 11.4 3.3 7.9 1.2 12.9-1.1 20.2-3.1 9.6 4.9 21 8 30.2 1.6 4.7 6.1 17.7 10.7 19.8 7.1 3.2 18.1-6.4 22.4-11.6 3.3-4.1 2.2-8.2 4.4-12 2.4-4.1 5.4-5.3 7.1-10.6 1.8-5.7 3.7-7.1 7.5-11.3 6.2-6.7 5.2-10.6 2.7-18.6-5.1-16.5 13.5-24.2 21.8-36.3 8.7-12.6-8.2-8.4-14.8-12.8-6.8-4.4-9.8-12.9-13.1-19.9s-6-17.5-11.4-23.3c-4.2-4.3-16.9-6.4-22.8-8.6zM609.2 428.8c-2.6 8.9-5.3 17.8-7.9 26.7-1.8 6.2-8.4 26.6-17.5 13.6-3.5-5-0.6-11.4 1.3-16 2.4-5.8-0.9-8.7 0.9-14.1 2-6.2 10-6.4 13.8-10.8 2.7-3 4.3-7.9 6.2-11.5 1 4 2.1 8.1 3.2 12.1z" fill="#F2B843" /><path d="M655.4 284.9c-28.5-49.4-78.7-78.5-131.6-82.4l-21.6 27.2-46.4 16.3-12.5 30.2 19.2 26.9 2-5.7c3.4-9.5 12.4-15.8 22.5-15.8h31.7l36.4 12.8 12.5 12v7.6l17.4 33.4 5.1-1.9c11-4 20.9-10.4 29.2-18.7l-4.2-6.3c-1.4-2 0.1-4.7 2.5-4.7h10.2c3 0 5.8 1.3 7.7 3.6l8.3 9.7c0.8 0.9 1.4 2 1.8 3.1l9.1 25.2 4.4-4.4c2.7-2.7 4.1-6.5 4.2-10.4 0-3.1 1.4-6.1 3.7-8.2l3.4-3c0.8-0.8 1.8-1.4 2.8-1.8-3.6-15.3-9.5-30.4-17.8-44.7zM407.6 291.3l7.9-8.5c5.8-6.2 7.4-15.2 4.2-23-3.4-8.3-10.9-13.6-19.2-14.8-29.2 26.5-47.4 62.2-52.6 100 6.2 5.4 12.6 11.2 12.6 11.7-0.1 1 23.2-2.9 23.2-2.9l-12-17.5 17.2-12.3 18.7-32.7zM423.8 456.4c7.5-2.4 11.6-10.4 9.1-17.9l-2.1-6.6c-0.9-2.9-0.9-5.9 0-8.8 2.7-8.1-2.4-16.8-10.8-18.4l-16.8-3.3-30.6-23.2-25.3 8.2c2.5 21.9 9.4 43.7 21.2 64.1 10.9 18.8 24.9 34.7 41 47.4l7.5-39.3 6.8-2.2z" fill="#2F9B77" /><path d="M844.6 337.9c12.4 0 22.4-10 22.4-22.4V234c0-12.4-10-22.4-22.4-22.4h-30.7V134c0-12.1-9.9-22-22-22H179.4c-12.4 0-22.4 10-22.4 22.4v716.1c0 12.4 10 22.4 22.4 22.4h63.4V942c0 3.4 1.7 6.5 4.5 8.3 2.8 1.9 6.3 2.2 9.4 0.9l38.9-16.5 39 16.5c1.2 0.5 2.6 0.8 3.9 0.8 1.9 0 3.9-0.6 5.5-1.7 2.8-1.9 4.5-5 4.5-8.3v-69.1h443.3c12.2 0 22.1-9.9 22.1-22.1V763h30.7c12.4 0 22.4-10 22.4-22.4v-81.5c0-12.4-10-22.4-22.4-22.4h-30.7v-86.2h30.7c12.4 0 22.4-10 22.4-22.4v-81.5c0-12.4-10-22.4-22.4-22.4h-30.7v-86.3h30.7z m0-106.3c1.3 0 2.4 1.1 2.4 2.4v81.5c0 1.3-1.1 2.4-2.4 2.4h-30.7v-86.3h30.7zM177 850.5V134.4c0-1.3 1.1-2.4 2.4-2.4h63.4v720.9h-63.4c-1.3 0-2.4-1.1-2.4-2.4z m151.5 76.4l-29-12.2c-2.5-1-5.3-1-7.8 0l-28.9 12.2v-54h65.7v54z m465.4-76.1c0 1.2-0.9 2.1-2.1 2.1h-529V132h529.1c1.1 0 2 0.9 2 2v716.8z m50.7-194.1c1.3 0 2.4 1.1 2.4 2.4v81.5c0 1.4-1 2.4-2.4 2.4h-30.7v-86.3h30.7z m0-212.5c1.3 0 2.4 1.1 2.4 2.4v81.5c0 1.3-1.1 2.4-2.4 2.4h-30.7v-86.3h30.7z" fill="#4D3500" /><path d="M680.3 600.1H343.7c-19.6 0-35.5 15.9-35.5 35.5s15.9 35.5 35.5 35.5h336.6c19.6 0 35.5-15.9 35.5-35.5s-15.9-35.5-35.5-35.5z m0 51H343.7c-8.5 0-15.5-7-15.5-15.5s7-15.5 15.5-15.5h336.6c8.5 0 15.5 7 15.5 15.5s-7 15.5-15.5 15.5zM680.3 718.8H343.7c-19.6 0-35.5 15.9-35.5 35.5s15.9 35.5 35.5 35.5h336.6c19.6 0 35.5-15.9 35.5-35.5s-15.9-35.5-35.5-35.5z m0 51H343.7c-8.5 0-15.5-7-15.5-15.5s7-15.5 15.5-15.5h336.6c8.5 0 15.5 7 15.5 15.5s-7 15.5-15.5 15.5zM512.3 543.2c29.8 0 59.9-7.6 87.5-23.5 40.6-23.4 69.7-61.3 81.9-106.7 12.2-45.4 6-92.7-17.5-133.3-23.5-40.6-61.4-69.7-106.7-81.8-45.3-12.1-92.7-5.9-133.3 17.6-40.6 23.5-69.7 61.4-81.9 106.7-12.2 45.3-6 92.7 17.5 133.3 32.6 56.3 91.7 87.7 152.5 87.7zM361.6 327.4c10.8-40.2 36.6-73.7 72.6-94.6 24-13.9 50.6-20.9 77.6-20.9 13.5 0 27.1 1.8 40.5 5.4 40.2 10.8 73.7 36.5 94.6 72.5 20.8 36 26.3 77.9 15.5 118.1-10.8 40.2-36.6 73.7-72.6 94.5-74.3 42.9-169.7 17.3-212.6-56.9-20.8-36-26.4-77.9-15.6-118.1z" fill="#4D3500" /></svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -0,0 +1 @@
<?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 class="icon" width="200px" height="200.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M72 440.5h880v286.2H72z" fill="#F2B843" /><path d="M72 726.6V834c0 19.8 16 35.8 35.8 35.8h808.5c19.8 0 35.8-16 35.8-35.8V726.6H72zM916.2 297.4H708.7L647 174.1c-6.1-12.1-18.5-19.8-32-19.8H408.9c-13.5 0-25.9 7.7-32 19.8l-61.7 123.3h-64.4v-35.8c0-19.8-16-35.8-35.8-35.8h-35.8c-19.8 0-35.8 16-35.8 35.8v35.8h-35.8c-19.8 0-35.8 16-35.8 35.8v107.3h880V333.1c0.2-19.7-15.8-35.7-35.6-35.7z" fill="#3DC38A" /><path d="M726.6 583.5c0 118.3-95.9 214.6-214.6 214.6-118.8 0-214.6-96.4-214.6-214.6 0-118.3 95.9-214.6 214.6-214.6 118.8 0 214.6 96.4 214.6 214.6z" fill="#EA800C" /><path d="M512 440.5c78.9 0 143.1 64.2 143.1 143.1S590.9 726.7 512 726.7s-143.1-64.2-143.1-143.1S433.1 440.5 512 440.5z" fill="#FFFFFF" /><path d="M773.1 386.8c9.9 0 17.9-8 17.9-17.9s-8-17.9-17.9-17.9-17.9 8-17.9 17.9c0.1 9.9 8.1 17.9 17.9 17.9zM565.7 207.9H458.3c-9.9 0-17.9 8-17.9 17.9s8 17.9 17.9 17.9h107.3c9.9 0 17.9-8 17.9-17.9s-8-17.9-17.8-17.9zM512 744.5c88.8 0 161-72.2 161-161s-72.2-161-161-161-161 72.2-161 161 72.2 161 161 161z m0-286.2c69 0 125.2 56.2 125.2 125.2S581 708.7 512 708.7s-125.2-56.2-125.2-125.2S443 458.3 512 458.3z" fill="#2F9B77" /><path d="M440.5 601.4c9.9 0 17.9-8 17.9-17.9 0-29.6 24.1-53.7 53.7-53.7 9.9 0 17.9-8 17.9-17.9s-8-17.9-17.9-17.9c-49.3 0-89.4 40.1-89.4 89.4-0.1 10 7.9 18 17.8 18z" fill="#3DC38A" /><path d="M844.7 386.8h35.8c9.9 0 17.9-8 17.9-17.9s-8-17.9-17.9-17.9h-35.8c-9.9 0-17.9 8-17.9 17.9s8 17.9 17.9 17.9z" fill="#2F9B77" /><path d="M773.1 396.8c15.4 0 27.9-12.5 27.9-27.9S788.5 341 773.1 341s-27.9 12.5-27.9 27.9v0.1c0.2 15.3 12.7 27.8 27.9 27.8z m0-35.8c4.4 0 7.9 3.5 7.9 7.9s-3.5 7.9-7.9 7.9c-4.3 0-7.8-3.6-7.9-8 0-4.3 3.6-7.8 7.9-7.8zM458.3 253.7h107.3c15.4 0 27.9-12.5 27.9-27.9s-12.5-27.9-27.8-27.9H458.3c-15.4 0-27.9 12.5-27.9 27.9s12.5 27.9 27.9 27.9z m0-35.8h107.4c4.3 0 7.8 3.5 7.8 7.9s-3.5 7.9-7.9 7.9H458.3c-4.4 0-7.9-3.5-7.9-7.9s3.5-7.9 7.9-7.9zM512 754.5c94.3 0 171-76.7 171-171s-76.7-171-171-171-171 76.7-171 171 76.7 171 171 171z m0-322c83.3 0 151 67.7 151 151s-67.7 151-151 151-151-67.7-151-151 67.7-151 151-151z" fill="#4D3500" /><path d="M512 718.7c74.5 0 135.2-60.7 135.2-135.2S586.5 448.3 512 448.3 376.8 509 376.8 583.5 437.5 718.7 512 718.7z m0-250.4c63.5 0 115.2 51.7 115.2 115.2S575.5 698.7 512 698.7 396.8 647 396.8 583.5 448.5 468.3 512 468.3z" fill="#4D3500" /><path d="M468.4 583.5c0-24.1 19.6-43.7 43.7-43.7 15.4 0 27.9-12.5 27.9-27.9S527.5 484 512.1 484c-54.8 0-99.4 44.6-99.4 99.4-0.1 7.5 2.8 14.5 8 19.8 5.3 5.3 12.3 8.2 19.8 8.2 15.4 0 27.9-12.5 27.9-27.9z m-35.7 0s0-0.1 0 0c0-43.9 35.6-79.5 79.4-79.5 4.4 0 7.9 3.5 7.9 7.9s-3.5 7.9-7.9 7.9c-35.1 0-63.7 28.6-63.7 63.7 0 4.4-3.5 7.9-7.9 7.9-2.1 0-4.1-0.8-5.6-2.3-1.4-1.5-2.2-3.5-2.2-5.6zM844.7 396.8h35.8c15.4 0 27.9-12.5 27.9-27.9S895.9 341 880.5 341h-35.8c-15.4 0-27.9 12.5-27.9 27.9s12.5 27.9 27.9 27.9z m0-35.8h35.8c4.4 0 7.9 3.5 7.9 7.9s-3.5 7.9-7.9 7.9h-35.8c-4.4 0-7.9-3.5-7.9-7.9s3.5-7.9 7.9-7.9z" fill="#4D3500" /><path d="M916.5 287.3H715.2l-58.9-117.8c-7.8-15.6-23.5-25.3-40.9-25.3H409.1c-17.4 0-33.1 9.7-40.9 25.3l-58.9 117.8H261v-25.8c0-25.3-20.5-45.8-45.8-45.8h-35.8c-25.3 0-45.8 20.5-45.8 45.8v25.8h-25.8c-25.3 0-45.8 20.5-45.8 45.8V834c0 25.1 20.5 45.7 45.8 45.8h808.4c25.3 0 45.8-20.5 45.8-45.8V442.8c0.2-0.8 0.3-1.6 0.3-2.4V333.1c0-25.3-20.5-45.8-45.8-45.8z m-737.1-51.6h35.8c14.2 0 25.8 11.6 25.8 25.8v25.9h-87.4v-25.9c0-14.2 11.6-25.8 25.8-25.8zM82 450.5h249.1c-27.5 37.3-43.7 83.3-43.7 133 0 49.8 16.3 95.8 43.8 133.1H82V450.5z m430-71.6c112.8 0 204.6 91.8 204.6 204.6S624.8 788.1 512 788.1s-204.6-91.8-204.6-204.6S399.2 378.9 512 378.9zM942 834c0 14.2-11.6 25.8-25.8 25.8H107.9C93.6 859.7 82 848.2 82 834v-97.4h265.8c41 44 99.4 71.5 164.2 71.5s123.1-27.5 164.2-71.5H942V834z m0-117.4H692.8c27.5-37.3 43.8-83.3 43.8-133.1 0-49.7-16.3-95.7-43.7-133H942v266.1z m0.3-286.2H676.2c-41-44-99.4-71.5-164.2-71.5s-123.2 27.6-164.3 71.6H82v-97.4c0-14.2 11.6-25.8 25.8-25.8h34.4c0.4 0.1 0.9 0.1 1.3 0.1h108c0.5 0 0.9 0 1.3-0.1h62.6c3.8 0 7.2-2.1 8.9-5.5L386 178.5c4.4-8.8 13.3-14.3 23.1-14.3h206.2c9.8 0 18.7 5.5 23.1 14.3l61.7 123.3c1.7 3.4 5.2 5.5 8.9 5.5h207.5c14.2 0 25.8 11.6 25.8 25.8v97.3z" fill="#4D3500" /></svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.3 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.5 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 300 KiB

View File

@@ -0,0 +1,33 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" baseProfile="full" width="100%" height="100%" viewBox="0 0 1400 800">
<rect x="1300" y="400" rx="40" ry="40" width="150" height="150" stroke="rgb(129, 201, 149)" fill="rgb(129, 201, 149)">
<animateTransform attributeType="XML" attributeName="transform" begin="0s" dur="35s" type="rotate" from="0 1450 550" to="360 1450 550" repeatCount="indefinite"/>
</rect>
<path d="M 100 350 A 150 150 0 1 1 400 350 Q400 370 380 370 L 250 370 L 120 370 Q100 370 100 350" fill="#a2b3ff">
<animateMotion path="M 800 -200 L 800 -300 L 800 -200" dur="20s" begin="0s" repeatCount="indefinite"/>
<animateTransform attributeType="XML" attributeName="transform" begin="0s" dur="30s" type="rotate" values="0 210 530 ; -30 210 530 ; 0 210 530" keyTimes="0 ; 0.5 ; 1" repeatCount="indefinite"/>
</path>
<circle cx="150" cy="150" r="180" stroke="#85FFBD" fill="#85FFBD">
<animateMotion path="M 0 0 L 40 20 Z" dur="5s" repeatCount="indefinite"/>
</circle>
<!-- 三角形 -->
<path d="M 165 580 L 270 580 Q275 578 270 570 L 223 483 Q220 480 217 483 L 165 570 Q160 578 165 580" fill="#a2b3ff">
<animateTransform attributeType="XML" attributeName="transform" begin="0s" dur="35s" type="rotate" from="0 210 530" to="360 210 530" repeatCount="indefinite"/>
</path>
<!-- <circle cx="1200" cy="600" r="30" stroke="rgb(241, 243, 244)" fill="rgb(241, 243, 244)">-->
<!-- <animateMotion path="M 0 0 L -20 40 Z" dur="9s" repeatCount="indefinite"/>-->
<!-- </circle>-->
<path d="M 100 350 A 40 40 0 1 1 180 350 L 180 430 A 40 40 0 1 1 100 430 Z" fill="#3054EB">
<animateMotion path="M 140 390 L 180 360 L 140 390" dur="20s" begin="0s" repeatCount="indefinite"/>
<animateTransform attributeType="XML" attributeName="transform" begin="0s" dur="30s" type="rotate" values="0 140 390; -60 140 390; 0 140 390" keyTimes="0 ; 0.5 ; 1" repeatCount="indefinite"/>
</path>
<rect x="400" y="600" rx="40" ry="40" width="100" height="100" stroke="rgb(129, 201, 149)" fill="#3054EB">
<animateTransform attributeType="XML" attributeName="transform" begin="0s" dur="35s" type="rotate" from="-30 550 750" to="330 550 750" repeatCount="indefinite"/>
</rect>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>

After

Width:  |  Height:  |  Size: 276 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

View File

@@ -1,19 +0,0 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
/* 申明字体为东方大楷 */
@font-face {
font-family: 'DongFangDaKai';
src: url('@/assets/font/DongFangDaKai-Regular.woff') format('woff'),
url('@/assets/font/DongFangDaKai-Regular.woff2') format('woff2');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: "MFBanHei"; /* Project id 1513211 */
src: url('@/assets/font/MFBanHei.ttf?t=1643094287456') format('truetype');
}

View File

@@ -0,0 +1,19 @@
<template>
<div class="not-container">
<img src="@/assets/images/403.png" class="not-img" alt="403" />
<div class="not-detail">
<h2>403</h2>
<h4>抱歉您无权访问该页面~🙅🙅</h4>
<el-button type="primary" @click="router.back"> 返回上一页 </el-button>
</div>
</div>
</template>
<script setup lang="ts" name="403">
import { useRouter } from "vue-router";
const router = useRouter();
</script>
<style scoped lang="scss">
@import "./index.scss";
</style>

View File

@@ -0,0 +1,19 @@
<template>
<div class="not-container">
<img src="@/assets/images/404.png" class="not-img" alt="404" />
<div class="not-detail">
<h2>404</h2>
<h4>抱歉您访问的页面不存在~🤷🤷</h4>
<el-button type="primary" @click="router.back"> 返回上一页 </el-button>
</div>
</div>
</template>
<script setup lang="ts" name="404">
import { useRouter } from "vue-router";
const router = useRouter();
</script>
<style scoped lang="scss">
@import "./index.scss";
</style>

View File

@@ -0,0 +1,19 @@
<template>
<div class="not-container">
<img src="@/assets/images/500.png" class="not-img" alt="500" />
<div class="not-detail">
<h2>500</h2>
<h4>抱歉您的网络不见了~🤦🤦</h4>
<el-button type="primary" @click="router.back"> 返回上一页 </el-button>
</div>
</div>
</template>
<script setup lang="ts" name="500">
import { useRouter } from "vue-router";
const router = useRouter();
</script>
<style scoped lang="scss">
@import "./index.scss";
</style>

View File

@@ -0,0 +1,32 @@
.not-container {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
.not-img {
margin-right: 120px;
}
.not-detail {
display: flex;
flex-direction: column;
h2,
h4 {
padding: 0;
margin: 0;
}
h2 {
font-size: 60px;
color: var(--el-text-color-primary);
}
h4 {
margin: 30px 0 20px;
font-size: 19px;
font-weight: normal;
color: var(--el-text-color-regular);
}
.el-button {
width: 100px;
}
}
}

View File

@@ -0,0 +1,45 @@
import { ElLoading } from "element-plus";
/* 全局请求 loading */
let loadingInstance: ReturnType<typeof ElLoading.service>;
/**
* @description 开启 Loading
* */
const startLoading = () => {
loadingInstance = ElLoading.service({
fullscreen: true,
lock: true,
text: "Loading",
background: "rgba(0, 0, 0, 0.7)"
});
};
/**
* @description 结束 Loading
* */
const endLoading = () => {
loadingInstance.close();
};
/**
* @description 显示全屏加载
* */
let needLoadingRequestCount = 0;
export const showFullScreenLoading = () => {
if (needLoadingRequestCount === 0) {
startLoading();
}
needLoadingRequestCount++;
};
/**
* @description 隐藏全屏加载
* */
export const tryHideFullScreenLoading = () => {
if (needLoadingRequestCount <= 0) return;
needLoadingRequestCount--;
if (needLoadingRequestCount === 0) {
endLoading();
}
};

View File

@@ -0,0 +1,67 @@
.loading-box {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
.loading-wrap {
display: flex;
align-items: center;
justify-content: center;
padding: 98px;
}
}
.dot {
position: relative;
box-sizing: border-box;
display: inline-block;
width: 32px;
height: 32px;
font-size: 32px;
transform: rotate(45deg);
animation: ant-rotate 1.2s infinite linear;
}
.dot i {
position: absolute;
display: block;
width: 14px;
height: 14px;
background-color: var(--el-color-primary);
border-radius: 100%;
opacity: 0.3;
transform: scale(0.75);
transform-origin: 50% 50%;
animation: ant-spin-move 1s infinite linear alternate;
}
.dot i:nth-child(1) {
top: 0;
left: 0;
}
.dot i:nth-child(2) {
top: 0;
right: 0;
animation-delay: 0.4s;
}
.dot i:nth-child(3) {
right: 0;
bottom: 0;
animation-delay: 0.8s;
}
.dot i:nth-child(4) {
bottom: 0;
left: 0;
animation-delay: 1.2s;
}
@keyframes ant-rotate {
to {
transform: rotate(405deg);
}
}
@keyframes ant-spin-move {
to {
opacity: 1;
}
}

View File

@@ -0,0 +1,13 @@
<template>
<div class="loading-box">
<div class="loading-wrap">
<span class="dot dot-spin"><i></i><i></i><i></i><i></i></span>
</div>
</div>
</template>
<script setup lang="ts" name="Loading"></script>
<style scoped lang="scss">
@import "./index.scss";
</style>

View File

@@ -0,0 +1,12 @@
<template>
<el-switch v-model="globalStore.isDark" inline-prompt :active-icon="Sunny" :inactive-icon="Moon" @change="switchDark" />
</template>
<script setup lang="ts" name="SwitchDark">
import { useTheme } from "@/hooks/useTheme";
import { useGlobalStore } from "@/stores/modules/global";
import { Sunny, Moon } from "@element-plus/icons-vue";
const { switchDark } = useTheme();
const globalStore = useGlobalStore();
</script>

View File

@@ -0,0 +1,19 @@
// ? 全局默认配置项
// 首页地址(默认)
export const HOME_URL: string = "/home/index";
// 登录页地址(默认)
export const LOGIN_URL: string = "/login";
// 默认主题颜色
export const DEFAULT_PRIMARY: string = "#003078";
// 路由白名单地址(本地存在的路由 staticRouter.ts 中)
export const ROUTER_WHITE_LIST: string[] = ["/500"];
// 高德地图 key
export const AMAP_MAP_KEY: string = "";
// 百度地图 key
export const BAIDU_MAP_KEY: string = "";

View File

@@ -0,0 +1,12 @@
import NProgress from "nprogress";
import "nprogress/nprogress.css";
NProgress.configure({
easing: "ease", // 动画方式
speed: 500, // 递增进度条的速度
showSpinner: true, // 是否显示加载ico
trickleSpeed: 200, // 自动递增间隔
minimum: 0.3 // 初始化时的最小百分比
});
export default NProgress;

View File

@@ -0,0 +1,28 @@
import { App, Directive } from "vue";
import auth from "./modules/auth";
import copy from "./modules/copy";
import waterMarker from "./modules/waterMarker";
import draggable from "./modules/draggable";
import debounce from "./modules/debounce";
import throttle from "./modules/throttle";
import longpress from "./modules/longpress";
const directivesList: { [key: string]: Directive } = {
auth,
copy,
waterMarker,
draggable,
debounce,
throttle,
longpress
};
const directives = {
install: function (app: App<Element>) {
Object.keys(directivesList).forEach(key => {
app.directive(key, directivesList[key]);
});
}
};
export default directives;

View File

@@ -0,0 +1,22 @@
/**
* v-auth
* 按钮权限指令
*/
import { useAuthStore } from "@/stores/modules/auth";
import type { Directive, DirectiveBinding } from "vue";
const auth: Directive = {
mounted(el: HTMLElement, binding: DirectiveBinding) {
const { value } = binding;
const authStore = useAuthStore();
const currentPageRoles = authStore.authButtonListGet[authStore.routeName] ?? [];
if (value instanceof Array && value.length) {
const hasPermission = value.every(item => currentPageRoles.includes(item));
if (!hasPermission) el.remove();
} else {
if (!currentPageRoles.includes(value)) el.remove();
}
}
};
export default auth;

View File

@@ -0,0 +1,37 @@
/**
* v-copy
* 复制某个值至剪贴板
* 接收参数string类型/Ref<string>类型/Reactive<string>类型
*/
import type { Directive, DirectiveBinding } from "vue";
import { ElMessage } from "element-plus";
interface ElType extends HTMLElement {
copyData: string | number;
__handleClick__: any;
}
const copy: Directive = {
mounted(el: ElType, binding: DirectiveBinding) {
el.copyData = binding.value;
el.addEventListener("click", handleClick);
},
updated(el: ElType, binding: DirectiveBinding) {
el.copyData = binding.value;
},
beforeUnmount(el: ElType) {
el.removeEventListener("click", el.__handleClick__);
}
};
async function handleClick(this: any) {
try {
await navigator.clipboard.writeText(this.copyData);
} catch (err) {
console.error("复制操作不被支持或失败: ", err);
}
ElMessage({
type: "success",
message: "复制成功"
});
}
export default copy;

View File

@@ -0,0 +1,31 @@
/**
* v-debounce
* 按钮防抖指令可自行扩展至input
* 接收参数function类型
*/
import type { Directive, DirectiveBinding } from "vue";
interface ElType extends HTMLElement {
__handleClick__: () => any;
}
const debounce: Directive = {
mounted(el: ElType, binding: DirectiveBinding) {
if (typeof binding.value !== "function") {
throw "callback must be a function";
}
let timer: NodeJS.Timeout | null = null;
el.__handleClick__ = function () {
if (timer) {
clearInterval(timer);
}
timer = setTimeout(() => {
binding.value();
}, 500);
};
el.addEventListener("click", el.__handleClick__);
},
beforeUnmount(el: ElType) {
el.removeEventListener("click", el.__handleClick__);
}
};
export default debounce;

View File

@@ -0,0 +1,49 @@
/*
需求:实现一个拖拽指令,可在父元素区域任意拖拽元素。
思路:
1、设置需要拖拽的元素为absolute其父元素为relative。
2、鼠标按下(onmousedown)时记录目标元素当前的 left 和 top 值。
3、鼠标移动(onmousemove)时计算每次移动的横向距离和纵向距离的变化值,并改变元素的 left 和 top 值
4、鼠标松开(onmouseup)时完成一次拖拽
使用:在 Dom 上加上 v-draggable 即可
<div class="dialog-model" v-draggable></div>
*/
import type { Directive } from "vue";
interface ElType extends HTMLElement {
parentNode: any;
}
const draggable: Directive = {
mounted: function (el: ElType) {
el.style.cursor = "move";
el.style.position = "absolute";
el.onmousedown = function (e) {
let disX = e.pageX - el.offsetLeft;
let disY = e.pageY - el.offsetTop;
document.onmousemove = function (e) {
let x = e.pageX - disX;
let y = e.pageY - disY;
let maxX = el.parentNode.offsetWidth - el.offsetWidth;
let maxY = el.parentNode.offsetHeight - el.offsetHeight;
if (x < 0) {
x = 0;
} else if (x > maxX) {
x = maxX;
}
if (y < 0) {
y = 0;
} else if (y > maxY) {
y = maxY;
}
el.style.left = x + "px";
el.style.top = y + "px";
};
document.onmouseup = function () {
document.onmousemove = document.onmouseup = null;
};
};
}
};
export default draggable;

View File

@@ -0,0 +1,49 @@
/**
* v-longpress
* 长按指令,长按时触发事件
*/
import type { Directive, DirectiveBinding } from "vue";
const directive: Directive = {
mounted(el: HTMLElement, binding: DirectiveBinding) {
if (typeof binding.value !== "function") {
throw "callback must be a function";
}
// 定义变量
let pressTimer: any = null;
// 创建计时器( 2秒后执行函数
const start = (e: any) => {
if (e.button) {
if (e.type === "click" && e.button !== 0) {
return;
}
}
if (pressTimer === null) {
pressTimer = setTimeout(() => {
handler(e);
}, 1000);
}
};
// 取消计时器
const cancel = () => {
if (pressTimer !== null) {
clearTimeout(pressTimer);
pressTimer = null;
}
};
// 运行函数
const handler = (e: MouseEvent | TouchEvent) => {
binding.value(e);
};
// 添加事件监听器
el.addEventListener("mousedown", start);
el.addEventListener("touchstart", start);
// 取消计时器
el.addEventListener("click", cancel);
el.addEventListener("mouseout", cancel);
el.addEventListener("touchend", cancel);
el.addEventListener("touchcancel", cancel);
}
};
export default directive;

View File

@@ -0,0 +1,41 @@
/*
需求:防止按钮在短时间内被多次点击,使用节流函数限制规定时间内只能点击一次。
思路:
1、第一次点击立即调用方法并禁用按钮等延迟结束再次激活按钮
2、将需要触发的方法绑定在指令上
使用:给 Dom 加上 v-throttle 及回调函数即可
<button v-throttle="debounceClick">节流提交</button>
*/
import type { Directive, DirectiveBinding } from "vue";
interface ElType extends HTMLElement {
__handleClick__: () => any;
disabled: boolean;
}
const throttle: Directive = {
mounted(el: ElType, binding: DirectiveBinding) {
if (typeof binding.value !== "function") {
throw "callback must be a function";
}
let timer: NodeJS.Timeout | null = null;
el.__handleClick__ = function () {
if (timer) {
clearTimeout(timer);
}
if (!el.disabled) {
el.disabled = true;
binding.value();
timer = setTimeout(() => {
el.disabled = false;
}, 1000);
}
};
el.addEventListener("click", el.__handleClick__);
},
beforeUnmount(el: ElType) {
el.removeEventListener("click", el.__handleClick__);
}
};
export default throttle;

View File

@@ -0,0 +1,36 @@
/*
需求:给整个页面添加背景水印。
思路:
1、使用 canvas 特性生成 base64 格式的图片文件,设置其字体大小,颜色等。
2、将其设置为背景图片从而实现页面或组件水印效果
使用:设置水印文案,颜色,字体大小即可
<div v-waterMarker="{text:'版权所有',textColor:'rgba(180, 180, 180, 0.4)'}"></div>
*/
import type { Directive, DirectiveBinding } from "vue";
const addWaterMarker: Directive = (str: string, parentNode: any, font: any, textColor: string) => {
// 水印文字,父元素,字体,文字颜色
let can: HTMLCanvasElement = document.createElement("canvas");
parentNode.appendChild(can);
can.width = 205;
can.height = 140;
can.style.display = "none";
let cans = can.getContext("2d") as CanvasRenderingContext2D;
cans.rotate((-20 * Math.PI) / 180);
cans.font = font || "16px Microsoft JhengHei";
cans.fillStyle = textColor || "rgba(180, 180, 180, 0.3)";
cans.textAlign = "left";
cans.textBaseline = "Middle" as CanvasTextBaseline;
cans.fillText(str, can.width / 10, can.height / 2);
parentNode.style.backgroundImage = "url(" + can.toDataURL("image/png") + ")";
};
const waterMarker = {
mounted(el: DirectiveBinding, binding: DirectiveBinding) {
addWaterMarker(binding.value.text, el, binding.value.font, binding.value.textColor);
}
};
export default waterMarker;

View File

@@ -0,0 +1,35 @@
/**
* @description请求配置
*/
export enum ResultEnum {
SUCCESS = "200",
ERROR = 500,
OVERDUE = "401",
TIMEOUT = 30000,
TYPE = "success"
}
/**
* @description请求方法
*/
export enum RequestEnum {
GET = "GET",
POST = "POST",
PATCH = "PATCH",
PUT = "PUT",
DELETE = "DELETE"
}
/**
* @description常用的 contentTyp 类型
*/
export enum ContentTypeEnum {
// json
JSON = "application/json;charset=UTF-8",
// text
TEXT = "text/plain;charset=UTF-8",
// form-data 一般配合qs
FORM_URLENCODED = "application/x-www-form-urlencoded;charset=UTF-8",
// form-data 上传
FORM_DATA = "multipart/form-data;charset=UTF-8"
}

View File

@@ -0,0 +1,32 @@
export namespace Table {
export interface Pageable {
pageNum: number;
pageSize: number;
total: number;
}
export interface StateProps {
tableData: any[];
pageable: Pageable;
searchParam: {
[key: string]: any;
};
searchInitParam: {
[key: string]: any;
};
totalParam: {
[key: string]: any;
};
icon?: {
[key: string]: any;
};
}
}
export namespace HandleData {
export type MessageType = "" | "success" | "warning" | "info" | "error";
}
export namespace Theme {
export type ThemeType = "light" | "inverted" | "dark";
export type GreyOrWeakType = "grey" | "weak";
}

View File

@@ -0,0 +1,22 @@
import { computed } from "vue";
import { useRoute } from "vue-router";
import { useAuthStore } from "@/stores/modules/auth";
/**
* @description 页面按钮权限
* */
export const useAuthButtons = () => {
const route = useRoute();
const authStore = useAuthStore();
const authButtons = authStore.authButtonListGet[route.name as string] || [];
const BUTTONS = computed(() => {
let currentPageAuthButton: { [key: string]: boolean } = {};
authButtons.forEach(item => (currentPageAuthButton[item] = true));
return currentPageAuthButton;
});
return {
BUTTONS
};
};

View File

@@ -0,0 +1,44 @@
import { ElNotification } from "element-plus";
/**
* @description 接收数据流生成 blob创建链接下载文件
* @param {Function} api 导出表格的api方法 (必传)
* @param {String} tempName 导出的文件名 (必传)
* @param {Object} params 导出的参数 (默认{})
* @param {Boolean} isNotify 是否有导出消息提示 (默认为 true)
* @param {String} fileType 导出的文件格式 (默认为.xlsx)
* */
export const useDownload = async (
api: (param: any) => Promise<any>,
tempName: string,
params: any = {},
isNotify: boolean = true,
fileType: string = ".xlsx"
) => {
if (isNotify) {
ElNotification({
title: "温馨提示",
message: "如果数据庞大会导致下载缓慢哦,请您耐心等待!",
type: "info",
duration: 3000
});
}
try {
const res = await api(params);
const blob = new Blob([res]);
// 兼容 edge 不支持 createObjectURL 方法
if ("msSaveOrOpenBlob" in navigator) return window.navigator.msSaveOrOpenBlob(blob, tempName + fileType);
const blobUrl = window.URL.createObjectURL(blob);
const exportFile = document.createElement("a");
exportFile.style.display = "none";
exportFile.download = `${tempName}${fileType}`;
exportFile.href = blobUrl;
document.body.appendChild(exportFile);
exportFile.click();
// 去除下载对 url 的影响
document.body.removeChild(exportFile);
window.URL.revokeObjectURL(blobUrl);
} catch (error) {
console.log(error);
}
};

View File

@@ -0,0 +1,34 @@
import { ElMessageBox, ElMessage } from "element-plus";
import { HandleData } from "./interface";
/**
* @description 操作单条数据信息 (二次确认【删除、禁用、启用、重置密码】)
* @param {Function} api 操作数据接口的api方法 (必传)
* @param {Object} params 携带的操作数据参数 {id,params} (必传)
* @param {String} message 提示信息 (必传)
* @param {String} confirmType icon类型 (不必传,默认为 warning)
* @returns {Promise}
*/
export const useHandleData = (
api: (params: any) => Promise<any>,
params: any = {},
message: string,
confirmType: HandleData.MessageType = "warning"
) => {
return new Promise((resolve, reject) => {
ElMessageBox.confirm(`是否${message}?`, "温馨提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: confirmType,
draggable: true
}).then(async () => {
const res = await api(params);
if (!res) return reject(false);
ElMessage({
type: "success",
message: `${message}成功!`
});
resolve(true);
});
});
};

View File

@@ -0,0 +1,27 @@
import { ref, onMounted, onUnmounted } from "vue";
/**
* @description 网络是否可用
* */
export const useOnline = () => {
const online = ref(true);
const showStatus = (val: any) => {
online.value = typeof val == "boolean" ? val : val.target.online;
};
// 在页面加载后,设置正确的网络状态
navigator.onLine ? showStatus(true) : showStatus(false);
onMounted(() => {
// 开始监听网络状态的变化
window.addEventListener("online", showStatus);
window.addEventListener("offline", showStatus);
});
onUnmounted(() => {
// 移除监听网络状态的变化
window.removeEventListener("online", showStatus);
window.removeEventListener("offline", showStatus);
});
return { online };
};

View File

@@ -0,0 +1,34 @@
import { ref, computed } from "vue";
/**
* @description 表格多选数据操作
* @param {String} rowKey 当表格可以多选时,所指定的 id
* */
export const useSelection = (rowKey: string = "id") => {
const isSelected = ref<boolean>(false);
const selectedList = ref<{ [key: string]: any }[]>([]);
// 当前选中的所有 ids 数组
const selectedListIds = computed((): string[] => {
let ids: string[] = [];
selectedList.value.forEach(item => ids.push(item[rowKey]));
return ids;
});
/**
* @description 多选操作
* @param {Array} rowArr 当前选择的所有数据
* @return void
*/
const selectionChange = (rowArr: { [key: string]: any }[]) => {
rowArr.length ? (isSelected.value = true) : (isSelected.value = false);
selectedList.value = rowArr;
};
return {
isSelected,
selectedList,
selectedListIds,
selectionChange
};
};

View File

@@ -0,0 +1,154 @@
import { Table } from "./interface";
import { reactive, computed, toRefs } from "vue";
/**
* @description table 页面操作方法封装
* @param {Function} api 获取表格数据 api 方法 (必传)
* @param {Object} initParam 获取数据初始化参数 (非必传,默认为{})
* @param {Boolean} isPageable 是否有分页 (非必传默认为true)
* @param {Function} dataCallBack 对后台返回的数据进行处理的方法 (非必传)
* */
export const useTable = (
api?: (params: any) => Promise<any>,
initParam: object = {},
isPageable: boolean = true,
dataCallBack?: (data: any) => any,
requestError?: (error: any) => void
) => {
const state = reactive<Table.StateProps>({
// 表格数据
tableData: [],
// 分页数据
pageable: {
// 当前页数
pageNum: 1,
// 每页显示条数
pageSize: 10,
// 总条数
total: 0
},
// 查询参数(只包括查询)
searchParam: {},
// 初始化默认的查询参数
searchInitParam: {},
// 总参数(包含分页和查询参数)
totalParam: {}
});
/**
* @description 分页查询参数(只包括分页和表格字段排序,其他排序方式可自行配置)
* */
const pageParam = computed({
get: () => {
return {
pageNum: state.pageable.pageNum,
pageSize: state.pageable.pageSize
};
},
set: (newVal: any) => {
console.log("我是分页更新之后的值", newVal);
}
});
/**
* @description 获取表格数据
* @return void
* */
const getTableList = async () => {
if (!api) return;
try {
// 先把初始化参数和分页参数放到总参数里面
Object.assign(state.totalParam, initParam, isPageable ? pageParam.value : {});
let { data } = await api({ ...state.searchInitParam, ...state.totalParam });
dataCallBack && (data = dataCallBack(data));
state.tableData = isPageable ? data.list : data;
// 解构后台返回的分页数据 (如果有分页更新分页信息)
if (isPageable) {
const { pageNum, pageSize, total } = data;
updatePageable({ pageNum, pageSize, total });
}
} catch (error) {
requestError && requestError(error);
}
};
/**
* @description 更新查询参数
* @return void
* */
const updatedTotalParam = () => {
state.totalParam = {};
// 处理查询参数,可以给查询参数加自定义前缀操作
let nowSearchParam: Table.StateProps["searchParam"] = {};
// 防止手动清空输入框携带参数(这里可以自定义查询参数前缀)
for (let key in state.searchParam) {
// 某些情况下参数为 false/0 也应该携带参数
if (state.searchParam[key] || state.searchParam[key] === false || state.searchParam[key] === 0) {
nowSearchParam[key] = state.searchParam[key];
}
}
Object.assign(state.totalParam, nowSearchParam, isPageable ? pageParam.value : {});
};
/**
* @description 更新分页信息
* @param {Object} pageable 后台返回的分页数据
* @return void
* */
const updatePageable = (pageable: Table.Pageable) => {
Object.assign(state.pageable, pageable);
};
/**
* @description 表格数据查询
* @return void
* */
const search = () => {
state.pageable.pageNum = 1;
updatedTotalParam();
getTableList();
};
/**
* @description 表格数据重置
* @return void
* */
const reset = () => {
state.pageable.pageNum = 1;
// 重置搜索表单的时,如果有默认搜索参数,则重置默认的搜索参数
state.searchParam = { ...state.searchInitParam };
updatedTotalParam();
getTableList();
};
/**
* @description 每页条数改变
* @param {Number} val 当前条数
* @return void
* */
const handleSizeChange = (val: number) => {
state.pageable.pageNum = 1;
state.pageable.pageSize = val;
getTableList();
};
/**
* @description 当前页改变
* @param {Number} val 当前页
* @return void
* */
const handleCurrentChange = (val: number) => {
state.pageable.pageNum = val;
getTableList();
};
return {
...toRefs(state),
getTableList,
search,
reset,
handleSizeChange,
handleCurrentChange,
updatedTotalParam
};
};

View File

@@ -0,0 +1,111 @@
import { storeToRefs } from "pinia";
import { Theme } from "./interface";
import { ElMessage } from "element-plus";
import { DEFAULT_PRIMARY } from "@/config";
import { useGlobalStore } from "@/stores/modules/global";
import { getLightColor, getDarkColor } from "@/utils/color";
import { menuTheme } from "@/styles/theme/menu";
import { asideTheme } from "@/styles/theme/aside";
import { headerTheme } from "@/styles/theme/header";
/**
* @description 全局主题 hooks
* */
export const useTheme = () => {
const globalStore = useGlobalStore();
const { primary, isDark, isGrey, isWeak, layout, asideInverted, headerInverted } = storeToRefs(globalStore);
// 切换暗黑模式 ==> 同时修改主题颜色、侧边栏、头部颜色
const switchDark = () => {
const html = document.documentElement as HTMLElement;
if (isDark.value) html.setAttribute("class", "dark");
else html.setAttribute("class", "");
changePrimary(primary.value);
setAsideTheme();
setHeaderTheme();
};
// 修改主题颜色
const changePrimary = (val: string | null) => {
if (!val) {
val = DEFAULT_PRIMARY;
ElMessage({ type: "success", message: `主题颜色已重置为 ${DEFAULT_PRIMARY}` });
}
// 计算主题颜色变化
document.documentElement.style.setProperty("--el-color-primary", val);
document.documentElement.style.setProperty(
"--el-color-primary-dark-2",
isDark.value ? `${getLightColor(val, 0.2)}` : `${getDarkColor(val, 0.3)}`
);
for (let i = 1; i <= 9; i++) {
const primaryColor = isDark.value ? `${getDarkColor(val, i / 10)}` : `${getLightColor(val, i / 10)}`;
document.documentElement.style.setProperty(`--el-color-primary-light-${i}`, primaryColor);
}
globalStore.setGlobalState("primary", val);
};
// 灰色和弱色切换
const changeGreyOrWeak = (type: Theme.GreyOrWeakType, value: boolean) => {
const body = document.body as HTMLElement;
if (!value) return body.removeAttribute("style");
const styles: Record<Theme.GreyOrWeakType, string> = {
grey: "filter: grayscale(1)",
weak: "filter: invert(80%)"
};
body.setAttribute("style", styles[type]);
const propName = type === "grey" ? "isWeak" : "isGrey";
globalStore.setGlobalState(propName, false);
};
// 设置菜单样式
const setMenuTheme = () => {
let type: Theme.ThemeType = "light";
if (layout.value === "transverse" && headerInverted.value) type = "inverted";
if (layout.value !== "transverse" && asideInverted.value) type = "inverted";
if (isDark.value) type = "dark";
const theme = menuTheme[type!];
for (const [key, value] of Object.entries(theme)) {
document.documentElement.style.setProperty(key, value);
}
};
// 设置侧边栏样式
const setAsideTheme = () => {
let type: Theme.ThemeType = "light";
if (asideInverted.value) type = "inverted";
if (isDark.value) type = "dark";
const theme = asideTheme[type!];
for (const [key, value] of Object.entries(theme)) {
document.documentElement.style.setProperty(key, value);
}
setMenuTheme();
};
// 设置头部样式
const setHeaderTheme = () => {
let type: Theme.ThemeType = "light";
if (headerInverted.value) type = "inverted";
if (isDark.value) type = "dark";
const theme = headerTheme[type!];
for (const [key, value] of Object.entries(theme)) {
document.documentElement.style.setProperty(key, value);
}
setMenuTheme();
};
// init theme
const initTheme = () => {
switchDark();
if (isGrey.value) changeGreyOrWeak("grey", true);
if (isWeak.value) changeGreyOrWeak("weak", true);
};
return {
initTheme,
switchDark,
changePrimary,
changeGreyOrWeak,
setAsideTheme,
setHeaderTheme
};
};

View File

@@ -0,0 +1,38 @@
import { ref } from "vue";
/**
* @description 获取本地时间
*/
export const useTime = () => {
const year = ref(0); // 年份
const month = ref(0); // 月份
const week = ref(""); // 星期几
const day = ref(0); // 天数
const hour = ref<number | string>(0); // 小时
const minute = ref<number | string>(0); // 分钟
const second = ref<number | string>(0); // 秒
const nowTime = ref<string>(""); // 当前时间
// 更新时间
const updateTime = () => {
const date = new Date();
year.value = date.getFullYear();
month.value = date.getMonth() + 1;
week.value = "日一二三四五六".charAt(date.getDay());
day.value = date.getDate();
hour.value =
(date.getHours() + "")?.padStart(2, "0") ||
new Intl.NumberFormat(undefined, { minimumIntegerDigits: 2 }).format(date.getHours());
minute.value =
(date.getMinutes() + "")?.padStart(2, "0") ||
new Intl.NumberFormat(undefined, { minimumIntegerDigits: 2 }).format(date.getMinutes());
second.value =
(date.getSeconds() + "")?.padStart(2, "0") ||
new Intl.NumberFormat(undefined, { minimumIntegerDigits: 2 }).format(date.getSeconds());
nowTime.value = `${year.value}${month.value}${day.value} ${hour.value}:${minute.value}:${second.value}`;
};
updateTime();
return { year, month, day, hour, minute, second, week, nowTime };
};

View File

@@ -0,0 +1,18 @@
import { createI18n } from "vue-i18n";
import { getBrowserLang } from "@/utils";
import zh from "./modules/zh";
import en from "./modules/en";
const i18n = createI18n({
// Use Composition API, Set to false
allowComposition: true,
legacy: false,
locale: getBrowserLang(),
messages: {
zh,
en
}
});
export default i18n;

View File

@@ -0,0 +1,29 @@
export default {
home: {
welcome: "Welcome"
},
tabs: {
refresh: "Refresh",
maximize: "Maximize",
closeCurrent: "Close current",
closeLeft: "Close Left",
closeRight: "Close Right",
closeOther: "Close other",
closeAll: "Close All"
},
header: {
componentSize: "Component size",
language: "Language",
theme: "theme",
layoutConfig: "Layout config",
primary: "primary",
darkMode: "Dark Mode",
greyMode: "Grey mode",
weakMode: "Weak mode",
fullScreen: "Full Screen",
exitFullScreen: "Exit Full Screen",
personalData: "Personal Data",
changePassword: "Change Password",
logout: "Logout"
}
};

View File

@@ -0,0 +1,29 @@
export default {
home: {
welcome: "欢迎使用"
},
tabs: {
refresh: "刷新",
maximize: "最大化",
closeCurrent: "关闭当前",
closeLeft: "关闭左侧",
closeRight: "关闭右侧",
closeOther: "关闭其它",
closeAll: "关闭所有"
},
header: {
componentSize: "组件大小",
language: "国际化",
theme: "全局主题",
layoutConfig: "布局设置",
primary: "primary",
darkMode: "暗黑模式",
greyMode: "灰色模式",
weakMode: "色弱模式",
fullScreen: "全屏",
exitFullScreen: "退出全屏",
personalData: "个人信息",
changePassword: "修改密码",
logout: "退出登录"
}
};

View File

@@ -1,92 +0,0 @@
<!--主界面的头部包含了logo导航用户信息-->
<template>
<el-menu
:default-active='activeIndex'
popper-effect='light'
mode='horizontal'
:router='false'
class='el-menu-njcn text-center'
:ellipsis='false'
background-color='var(--el-color-primary)'
text-color='#fff'
active-text-color='#ffd04b'
@select='handleSelect'
:style="{height:headerHeight+'px'}"
>
<img
class='float-left'
:style="{height:headerHeight+'px'}"
src='@/assets/images/logo.png'
/>
<el-menu-item index='0'>
<el-icon><HomeFilled /></el-icon>
<span>运营管理</span>
</el-menu-item>
<el-sub-menu index='1' :popper-offset="0">
<template #title>
<el-icon><Monitor /></el-icon>
台账管理
</template>
<el-menu-item index='/home1'>脚本检测管理</el-menu-item>
<el-menu-item index='/home1'>被检设备管理</el-menu-item>
<el-menu-item index='/home1'>误差体系管理</el-menu-item>
<el-menu-item index='/home1'>检测源管理</el-menu-item>
</el-sub-menu>
<el-sub-menu index='2' :popper-offset="0">
<template #title>
<el-icon><UserFilled /></el-icon>
权限管理
</template>
<el-menu-item index='/home2'>用户管理</el-menu-item>
<el-menu-item index='/home2'>角色管理</el-menu-item>
<el-menu-item index='/home2'>菜单管理</el-menu-item>
</el-sub-menu>
<el-sub-menu index='3' :popper-offset="0">
<template #title>
<el-icon><Setting /></el-icon>
系统配置
</template>
<el-menu-item index='/home3'>系统配置</el-menu-item>
<el-menu-item index='/home3'>数据字典</el-menu-item>
<el-menu-item index='/home3'>报告模板</el-menu-item>
<el-menu-item index='/home3'>版本注册</el-menu-item>
</el-sub-menu>
<el-menu-item index='0'>
<el-icon><Document /></el-icon>
日志管理
</el-menu-item>
<el-menu-item index='0'>
<el-icon><DataLine /></el-icon>
统计分析
</el-menu-item>
</el-menu>
</template>
<script setup lang='ts'>
defineOptions({
name: 'customHeader',
})
import { ref, onMounted } from 'vue'
const activeIndex = ref('1')
const handleSelect = (key: string, keyPath: string[]) => {
console.log(key)
}
defineProps(['headerHeight'])
</script>
<style scoped>
/* 浮动菜单的颜色 */
.el-menu-item:hover {
--el-menu-hover-bg-color: var(--el-color-primary-light-3);
}
.el-menu--horizontal > .el-menu-item:nth-child(1) {
margin-right: auto;
}
.el-menu--horizontal.el-menu {
border-bottom: solid 0 var(--el-menu-border-color);
}
</style>

View File

@@ -0,0 +1,60 @@
.el-container {
width: 100%;
height: 100%;
:deep(.el-header) {
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: space-between;
height: 55px;
padding: 0 15px 0 0;
background-color: var(--el-header-bg-color);
border-bottom: 1px solid var(--el-header-border-color);
.header-lf {
display: flex;
align-items: center;
overflow: hidden;
white-space: nowrap;
.logo {
flex-shrink: 0;
width: 210px;
margin-right: 16px;
.logo-img {
width: 28px;
object-fit: contain;
margin-right: 6px;
}
.logo-text {
font-size: 21.5px;
font-weight: bold;
color: var(--el-header-logo-text-color);
white-space: nowrap;
}
}
}
}
.classic-content {
display: flex;
height: calc(100% - 55px);
:deep(.el-aside) {
width: auto;
background-color: var(--el-menu-bg-color);
border-right: 1px solid var(--el-aside-border-color);
.aside-box {
display: flex;
flex-direction: column;
height: 100%;
transition: width 0.3s ease;
.el-menu {
width: 100%;
overflow-x: hidden;
border-right: none;
}
}
}
.classic-main {
display: flex;
flex-direction: column;
}
}
}

View File

@@ -0,0 +1,62 @@
<!-- 经典布局 -->
<template>
<el-container class="layout">
<el-header>
<div class="header-lf mask-image">
<div class="logo flx-center">
<img class="logo-img" src="@/assets/images/logo.svg" alt="logo" />
<span class="logo-text">{{ title }}</span>
</div>
<ToolBarLeft />
</div>
<div class="header-ri">
<ToolBarRight />
</div>
</el-header>
<el-container class="classic-content">
<el-aside>
<div class="aside-box" :style="{ width: isCollapse ? '65px' : '210px' }">
<el-scrollbar>
<el-menu
:router="false"
:default-active="activeMenu"
:collapse="isCollapse"
:unique-opened="accordion"
:collapse-transition="false"
>
<SubMenu :menu-list="menuList" />
</el-menu>
</el-scrollbar>
</div>
</el-aside>
<el-container class="classic-main">
<Main />
</el-container>
</el-container>
</el-container>
</template>
<script setup lang="ts" name="layoutClassic">
import { computed } from "vue";
import { useRoute } from "vue-router";
import { useAuthStore } from "@/stores/modules/auth";
import { useGlobalStore } from "@/stores/modules/global";
import Main from "@/layouts/components/Main/index.vue";
import SubMenu from "@/layouts/components/Menu/SubMenu.vue";
import ToolBarLeft from "@/layouts/components/Header/ToolBarLeft.vue";
import ToolBarRight from "@/layouts/components/Header/ToolBarRight.vue";
const title = import.meta.env.VITE_GLOB_APP_TITLE;
const route = useRoute();
const authStore = useAuthStore();
const globalStore = useGlobalStore();
const accordion = computed(() => globalStore.accordion);
const isCollapse = computed(() => globalStore.isCollapse);
const menuList = computed(() => authStore.showMenuListGet);
const activeMenu = computed(() => (route.meta.activeMenu ? route.meta.activeMenu : route.path) as string);
</script>
<style scoped lang="scss">
@import "./index.scss";
</style>

View File

@@ -0,0 +1,95 @@
.el-container {
width: 100%;
height: 100%;
.aside-split {
display: flex;
flex-direction: column;
flex-shrink: 0;
width: 70px;
height: 100%;
background-color: var(--el-menu-bg-color);
border-right: 1px solid var(--el-aside-border-color);
.logo {
box-sizing: border-box;
height: 55px;
.logo-img {
width: 32px;
object-fit: contain;
}
}
.el-scrollbar {
height: calc(100% - 55px);
.split-list {
flex: 1;
.split-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 70px;
cursor: pointer;
transition: all 0.3s ease;
.el-icon {
font-size: 20px;
}
.title {
margin-top: 6px;
font-size: 12px;
}
.el-icon,
.title {
color: var(--el-menu-text-color);
}
}
.split-active {
background-color: var(--el-color-primary) !important;
.el-icon,
.title {
color: #ffffff !important;
}
}
}
}
}
.not-aside {
width: 0 !important;
border-right: none !important;
}
.el-aside {
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
background-color: var(--el-menu-bg-color);
border-right: 1px solid var(--el-aside-border-color);
transition: width 0.3s ease;
.el-scrollbar {
height: calc(100% - 55px);
.el-menu {
width: 100%;
overflow-x: hidden;
border-right: none;
}
}
.logo {
box-sizing: border-box;
height: 55px;
.logo-text {
font-size: 24px;
font-weight: bold;
color: var(--el-aside-logo-text-color);
white-space: nowrap;
}
}
}
.el-header {
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: space-between;
height: 55px;
padding: 0 15px;
background-color: var(--el-header-bg-color);
border-bottom: 1px solid var(--el-border-color-light);
}
}

Some files were not shown because too many files have changed in this diff Show More