新架构
|
@ -1,12 +1,14 @@
|
||||||
|
VITE_APP_NAME=保安管理
|
||||||
VITE_APP_ENV=development
|
VITE_APP_ENV=development
|
||||||
VITE_APP_PORT=9527
|
VITE_APP_PORT=9527
|
||||||
VITE_DROP_CONSOLE=false
|
VITE_DROP_CONSOLE=false
|
||||||
|
|
||||||
# axios
|
# axios
|
||||||
VITE_APP_BASE_API=/api
|
VITE_APP_BASE_API=/api
|
||||||
VITE_APP_PROXY_URL=http://172.10.10.93:1233
|
# VITE_APP_PROXY_URL=121321
|
||||||
|
|
||||||
#crypto js 前后端需保持一致
|
#jsencrypt 跟后端公钥保持一致
|
||||||
# VITE_APP_CRYPTO_JS_SECRET_KEY=f0234d57c311beb2
|
VITE_APP_JS_ENCRYPT_PUBLIC_KEY=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCo58dwFmtTkiA4bj/FiBqlYsiKOugT/WCQRYFPY49A7y6VEswzMihgxSz0B/UDdekM9PDsKy06Gjy8BpWu+ikR1ms+/PbMJOmw5jGUeiswf8uqJDDquHG5oJJk0o7J8/1JvzmpbN/Ctjcm2yUscTfG2WvvY0ViwnptYU7+ZkRcuQIDAQAB
|
||||||
# VITE_APP_CRYPTO_JS_SECRET_IV=eb7905b31669ad1e
|
|
||||||
VITE_APP_CRYPTO_JS_SECRET_KEY=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCo58dwFmtTkiA4bj/FiBqlYsiKOugT/WCQRYFPY49A7y6VEswzMihgxSz0B/UDdekM9PDsKy06Gjy8BpWu+ikR1ms+/PbMJOmw5jGUeiswf8uqJDDquHG5oJJk0o7J8/1JvzmpbN/Ctjcm2yUscTfG2WvvY0ViwnptYU7+ZkRcuQIDAQAB
|
# minio
|
||||||
|
VITE_APP_MINIO_URL=http://172.10.10.238:9000
|
|
@ -1,12 +1,12 @@
|
||||||
VITE_APP_ENV=production
|
VITE_APP_NAME=保安管理
|
||||||
VITE_APP_PORT=9528
|
VITE_APP_ENV=development
|
||||||
VITE_DROP_CONSOLE=true
|
VITE_APP_PORT=9527
|
||||||
|
VITE_DROP_CONSOLE=false
|
||||||
|
|
||||||
# axios
|
# axios
|
||||||
VITE_APP_BASE_API=/api
|
VITE_APP_BASE_API=/api
|
||||||
VITE_APP_PROXY_URL=https://localhost:8083
|
VITE_APP_PROXY_URL='http://172.10.10.93:1233'
|
||||||
|
VITE_APP_JS_ENCRYPT_PUBLIC_KEY=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCo58dwFmtTkiA4bj/FiBqlYsiKOugT/WCQRYFPY49A7y6VEswzMihgxSz0B/UDdekM9PDsKy06Gjy8BpWu+ikR1ms+/PbMJOmw5jGUeiswf8uqJDDquHG5oJJk0o7J8/1JvzmpbN/Ctjcm2yUscTfG2WvvY0ViwnptYU7+ZkRcuQIDAQAB
|
||||||
|
|
||||||
#crypto js 前后端需保持一致
|
# minio
|
||||||
# VITE_APP_CRYPTO_JS_SECRET_KEY=f0234d57c311beb2
|
VITE_APP_MINIO_URL=http://172.10.10.238:9000
|
||||||
# VITE_APP_CRYPTO_JS_SECRET_IV=eb7905b31669ad1e
|
|
||||||
VITE_APP_CRYPTO_JS_SECRET_KEY=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCo58dwFmtTkiA4bj/FiBqlYsiKOugT/WCQRYFPY49A7y6VEswzMihgxSz0B/UDdekM9PDsKy06Gjy8BpWu+ikR1ms+/PbMJOmw5jGUeiswf8uqJDDquHG5oJJk0o7J8/1JvzmpbN/Ctjcm2yUscTfG2WvvY0ViwnptYU7+ZkRcuQIDAQAB
|
|
|
@ -8,17 +8,32 @@ pnpm-debug.log*
|
||||||
lerna-debug.log*
|
lerna-debug.log*
|
||||||
|
|
||||||
node_modules
|
node_modules
|
||||||
|
.DS_Store
|
||||||
dist
|
dist
|
||||||
dist-ssr
|
dist-ssr
|
||||||
|
coverage
|
||||||
*.local
|
*.local
|
||||||
|
|
||||||
|
/cypress/videos/
|
||||||
|
/cypress/screenshots/
|
||||||
|
|
||||||
# Editor directories and files
|
# Editor directories and files
|
||||||
.vscode/*
|
.vscode/*
|
||||||
!.vscode/extensions.json
|
!.vscode/extensions.json
|
||||||
.idea
|
.idea
|
||||||
.DS_Store
|
|
||||||
*.suo
|
*.suo
|
||||||
*.ntvs*
|
*.ntvs*
|
||||||
*.njsproj
|
*.njsproj
|
||||||
*.sln
|
*.sln
|
||||||
*.sw?
|
*.sw?
|
||||||
|
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
analyze.html
|
||||||
|
auto-imports.d.ts
|
||||||
|
components.d.ts
|
||||||
|
yarn.lock
|
||||||
|
package-lock.json
|
||||||
|
*.zip
|
||||||
|
.env.locality
|
||||||
|
analyzer.html
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"vueIndentScriptAndStyle": true,
|
||||||
|
|
||||||
|
"htmlWhitespaceSensitivity": "ignore",
|
||||||
|
|
||||||
|
"printWidth": 80,
|
||||||
|
|
||||||
|
"singleAttributePerLine": true
|
||||||
|
}
|
35
README.md
|
@ -1,18 +1,33 @@
|
||||||
# Vue 3 + TypeScript + Vite
|
# management
|
||||||
|
|
||||||
This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
This template should help get you started developing with Vue 3 in Vite.
|
||||||
|
|
||||||
## Recommended IDE Setup
|
## Recommended IDE Setup
|
||||||
|
|
||||||
- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
|
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
|
||||||
|
|
||||||
## Type Support For `.vue` Imports in TS
|
## Type Support for `.vue` Imports in TS
|
||||||
|
|
||||||
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types.
|
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types.
|
||||||
|
|
||||||
If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps:
|
## Customize configuration
|
||||||
|
|
||||||
1. Disable the built-in TypeScript Extension
|
See [Vite Configuration Reference](https://vite.dev/config/).
|
||||||
1. Run `Extensions: Show Built-in Extensions` from VSCode's command palette
|
|
||||||
2. Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)`
|
## Project Setup
|
||||||
2. Reload the VSCode window by running `Developer: Reload Window` from the command palette.
|
|
||||||
|
```sh
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compile and Hot-Reload for Development
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Type-Check, Compile and Minify for Production
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
17
index.html
|
@ -1,13 +1,16 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="">
|
||||||
<head>
|
|
||||||
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
<link rel="icon" href="/favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>管理后台</title>
|
<title>保安管理系统</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
|
||||||
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<script type="module" src="/src/main.ts"></script>
|
<script type="module" src="/src/main.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
61
package.json
|
@ -1,40 +1,47 @@
|
||||||
{
|
{
|
||||||
"name": "web",
|
"name": "management",
|
||||||
"private": true,
|
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite --mode development",
|
"dev": "vite --mode development",
|
||||||
"pro": "vite --mode production",
|
"local": "vite --mode locality",
|
||||||
"build": "vite build --mode production",
|
"build": "run-p type-check \"build-only {@}\" --",
|
||||||
"preview": "vite preview"
|
"preview": "vite preview",
|
||||||
|
"build-only": "vite build",
|
||||||
|
"type-check": "vue-tsc --build"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@amap/amap-jsapi-loader": "^1.0.1",
|
"@vueuse/core": "^13.1.0",
|
||||||
"@types/node": "^20.5.0",
|
"animate.css": "^4.1.1",
|
||||||
"@vitejs/plugin-vue-jsx": "^3.0.1",
|
"axios": "^1.8.1",
|
||||||
"ant-design-vue": "4.x",
|
"dayjs": "^1.11.13",
|
||||||
"axios": "^1.4.0",
|
"event-source-polyfill": "^1.0.31",
|
||||||
"crypto-js": "^4.1.1",
|
"js-base64": "^3.7.7",
|
||||||
"echarts": "^5.4.3",
|
|
||||||
"jsencrypt": "^3.3.2",
|
"jsencrypt": "^3.3.2",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"nprogress": "^0.2.0",
|
"pinia": "^3.0.1",
|
||||||
"pinia": "^2.1.6",
|
"pinia-plugin-persistedstate": "^4.2.0",
|
||||||
"pinia-plugin-persistedstate": "^3.2.0",
|
"sass": "^1.85.1",
|
||||||
"terser": "^5.19.2",
|
"terser": "^5.39.0",
|
||||||
"vue": "^3.3.4",
|
"unplugin-auto-import": "^19.1.1",
|
||||||
"vue-router": "4"
|
"unplugin-vue-components": "^28.4.1",
|
||||||
|
"vue": "^3.5.13",
|
||||||
|
"vue-router": "^4.5.0",
|
||||||
|
"naive-ui": "^2.41.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/crypto-js": "^4.1.1",
|
"@tsconfig/node22": "^22.0.0",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/event-source-polyfill": "^1.0.5",
|
||||||
"@types/nprogress": "^0.2.3",
|
"@types/node": "^22.13.4",
|
||||||
"@vitejs/plugin-vue": "^4.2.3",
|
"@vitejs/plugin-vue": "^5.2.1",
|
||||||
"less": "^4.2.0",
|
"@vitejs/plugin-vue-jsx": "^4.1.1",
|
||||||
"sass": "^1.65.1",
|
"@vue/tsconfig": "^0.7.0",
|
||||||
"typescript": "^5.0.2",
|
"npm-run-all2": "^7.0.2",
|
||||||
"vite": "^4.4.5",
|
"rollup-plugin-visualizer": "^5.14.0",
|
||||||
"vue-tsc": "^1.8.5"
|
"typescript": "~5.7.3",
|
||||||
|
"vite": "^6.1.0",
|
||||||
|
"vite-plugin-vue-devtools": "^7.7.2",
|
||||||
|
"vue-tsc": "^2.2.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
2099
pnpm-lock.yaml
After Width: | Height: | Size: 3.6 MiB |
After Width: | Height: | Size: 4.2 KiB |
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" 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="IconifyId1813088fe1fbc01fb467" 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(#IconifyId1813088fe1fbc01fb466)" 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(#IconifyId1813088fe1fbc01fb467)" 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>
|
|
Before Width: | Height: | Size: 1.5 KiB |
30
src/App.vue
|
@ -1,13 +1,25 @@
|
||||||
<template>
|
<template>
|
||||||
<!-- <a-config-provider> -->
|
<n-config-provider
|
||||||
|
:theme="settingsStore.theme"
|
||||||
|
:locale="zhCN"
|
||||||
|
:dateLocale="dateZhCN"
|
||||||
|
>
|
||||||
|
<n-loading-bar-provider>
|
||||||
|
<n-notification-provider>
|
||||||
|
<n-modal-provider>
|
||||||
|
<n-message-provider>
|
||||||
|
<n-dialog-provider>
|
||||||
<router-view></router-view>
|
<router-view></router-view>
|
||||||
<!-- </a-config-provider> -->
|
</n-dialog-provider>
|
||||||
|
</n-message-provider>
|
||||||
|
</n-modal-provider>
|
||||||
|
</n-notification-provider>
|
||||||
|
</n-loading-bar-provider>
|
||||||
|
</n-config-provider>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts"></script>
|
<script setup lang="ts">
|
||||||
|
import { useSettingsStore } from "@/stores/settings";
|
||||||
<style lang="scss">
|
const settingsStore = useSettingsStore();
|
||||||
body {
|
import { dateZhCN, zhCN } from "naive-ui";
|
||||||
font-family: AliBaBaPuHuTi, serif;
|
</script>
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
@font-face {
|
|
||||||
font-family: AliBaBaPuHuTi;
|
|
||||||
src: url('../font/AlibabaPuHuiTi-2-65-Medium.ttf');
|
|
||||||
}
|
|
|
@ -1,53 +0,0 @@
|
||||||
/* http://meyerweb.com/eric/tools/css/reset/ */
|
|
||||||
/* v1.0 | 20080212 */
|
|
||||||
|
|
||||||
html, body, div, span, applet, object, iframe,
|
|
||||||
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
|
||||||
a, abbr, acronym, address, big, cite, code,
|
|
||||||
del, dfn, em, font, img, ins, kbd, q, s, samp,
|
|
||||||
small, strike, strong, sub, sup, tt, var,
|
|
||||||
b, u, i, center,
|
|
||||||
dl, dt, dd, ol, ul, li,
|
|
||||||
fieldset, form, label, legend,
|
|
||||||
table, caption, tbody, tfoot, thead, tr, th, td {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
border: 0;
|
|
||||||
outline: 0;
|
|
||||||
font-size: 100%;
|
|
||||||
vertical-align: baseline;
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
ol, ul {
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
blockquote, q {
|
|
||||||
quotes: none;
|
|
||||||
}
|
|
||||||
blockquote:before, blockquote:after,
|
|
||||||
q:before, q:after {
|
|
||||||
content: '';
|
|
||||||
content: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* remember to define focus styles! */
|
|
||||||
:focus {
|
|
||||||
outline: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* remember to highlight inserts somehow! */
|
|
||||||
ins {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
del {
|
|
||||||
text-decoration: line-through;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* tables still need 'cellspacing="0"' in the markup */
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
border-spacing: 0;
|
|
||||||
}
|
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 5.3 KiB |
After Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 7.0 KiB |
|
@ -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 |
|
@ -1,19 +1,51 @@
|
||||||
/* flex */
|
/* flex */
|
||||||
.flx-center {
|
|
||||||
|
.flex-center {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
.flx-justify-between {
|
|
||||||
|
.flex-end {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-justify-between {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
.flx-align-center {
|
|
||||||
|
.flex-align-center {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.flex-column-center {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.w-f {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h-f {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.f-r {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.f-l {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
/* clearfix */
|
/* clearfix */
|
||||||
.clearfix::after {
|
.clearfix::after {
|
||||||
display: block;
|
display: block;
|
||||||
|
@ -49,11 +81,13 @@
|
||||||
.fade-transform-enter-active {
|
.fade-transform-enter-active {
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fade-transform-enter-from {
|
.fade-transform-enter-from {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
transform: translateX(-30px);
|
transform: translateX(-30px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.fade-transform-leave-to {
|
.fade-transform-leave-to {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
|
@ -64,34 +98,13 @@
|
||||||
.breadcrumb-enter-active {
|
.breadcrumb-enter-active {
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.breadcrumb-enter-from,
|
.breadcrumb-enter-from,
|
||||||
.breadcrumb-leave-active {
|
.breadcrumb-leave-active {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateX(10px);
|
transform: translateX(10px);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* scroll bar */
|
|
||||||
::-webkit-scrollbar {
|
|
||||||
width: 6px;
|
|
||||||
height: 6px;
|
|
||||||
}
|
|
||||||
::-webkit-scrollbar-thumb {
|
|
||||||
background-color: var(--el-border-color-darker);
|
|
||||||
border-radius: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* nprogress */
|
|
||||||
#nprogress .bar {
|
|
||||||
background: var(--el-color-primary) !important;
|
|
||||||
}
|
|
||||||
#nprogress .spinner-icon {
|
|
||||||
border-top-color: var(--el-color-primary) !important;
|
|
||||||
border-left-color: var(--el-color-primary) !important;
|
|
||||||
}
|
|
||||||
#nprogress .peg {
|
|
||||||
box-shadow: 0 0 10px var(--el-color-primary), 0 0 5px var(--el-color-primary) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 外边距、内边距全局样式 */
|
/* 外边距、内边距全局样式 */
|
||||||
@for $i from 0 through 40 {
|
@for $i from 0 through 40 {
|
||||||
.mt#{$i} {
|
.mt#{$i} {
|
||||||
|
@ -429,3 +442,46 @@
|
||||||
padding-top: 25px;
|
padding-top: 25px;
|
||||||
padding-bottom: 25px;
|
padding-bottom: 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*高德地图去水印*/
|
||||||
|
.amap-logo {
|
||||||
|
display: none !important;
|
||||||
|
visibility: hidden !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.amap-copyright {
|
||||||
|
display: none !important;
|
||||||
|
visibility: hidden !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*高德地图去水印 结束*/
|
||||||
|
|
||||||
|
/*浏览器滚动条样式*/
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 4px;
|
||||||
|
height: 8px;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
display: block;
|
||||||
|
min-height: 12px;
|
||||||
|
min-width: 8px;
|
||||||
|
border-radius: 6px;
|
||||||
|
background-color: rgb(217, 217, 217);
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
display: block;
|
||||||
|
min-height: 12px;
|
||||||
|
min-width: 8px;
|
||||||
|
border-radius: 6px;
|
||||||
|
background-color: rgb(159, 159, 159);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*浏览器滚动条样式 结束*/
|
||||||
|
|
||||||
|
/* 鼠标悬浮手指 */
|
||||||
|
.pointer {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
@font-face {
|
|
||||||
font-family: AliBaBaPuHuTi;
|
|
||||||
src: url('../font/AlibabaPuHuiTi-2-65-Medium.ttf');
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
@mixin wh($w, $h) {
|
|
||||||
width: $w;
|
|
||||||
height: $h;
|
|
||||||
}
|
|
|
@ -1,53 +0,0 @@
|
||||||
/* http://meyerweb.com/eric/tools/css/reset/ */
|
|
||||||
/* v1.0 | 20080212 */
|
|
||||||
|
|
||||||
html, body, div, span, applet, object, iframe,
|
|
||||||
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
|
||||||
a, abbr, acronym, address, big, cite, code,
|
|
||||||
del, dfn, em, font, img, ins, kbd, q, s, samp,
|
|
||||||
small, strike, strong, sub, sup, tt, var,
|
|
||||||
b, u, i, center,
|
|
||||||
dl, dt, dd, ol, ul, li,
|
|
||||||
fieldset, form, label, legend,
|
|
||||||
table, caption, tbody, tfoot, thead, tr, th, td {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
border: 0;
|
|
||||||
outline: 0;
|
|
||||||
font-size: 100%;
|
|
||||||
vertical-align: baseline;
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
ol, ul {
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
blockquote, q {
|
|
||||||
quotes: none;
|
|
||||||
}
|
|
||||||
blockquote:before, blockquote:after,
|
|
||||||
q:before, q:after {
|
|
||||||
content: '';
|
|
||||||
content: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* remember to define focus styles! */
|
|
||||||
:focus {
|
|
||||||
outline: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* remember to highlight inserts somehow! */
|
|
||||||
ins {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
del {
|
|
||||||
text-decoration: line-through;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* tables still need 'cellspacing="0"' in the markup */
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
border-spacing: 0;
|
|
||||||
}
|
|
|
@ -1,2 +0,0 @@
|
||||||
/* global css variable */
|
|
||||||
$primary-color: var(--el-color-primary);
|
|
|
@ -1,2 +0,0 @@
|
||||||
/* 存放css变量 */
|
|
||||||
$primary-color: red;
|
|
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><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>
|
|
Before Width: | Height: | Size: 496 B |
|
@ -1,22 +1,18 @@
|
||||||
import axios, {
|
import axios, {
|
||||||
AxiosError,
|
AxiosError,
|
||||||
AxiosInstance,
|
type AxiosInstance,
|
||||||
AxiosRequestConfig,
|
type AxiosRequestConfig,
|
||||||
AxiosResponse,
|
type AxiosResponse,
|
||||||
InternalAxiosRequestConfig,
|
|
||||||
} from "axios";
|
} from "axios";
|
||||||
import { message } from "ant-design-vue";
|
import router from "@/router";
|
||||||
import { useUserStore } from "@/stores/modules/userStore";
|
import Loading from "@/shared/classes/Loading";
|
||||||
import { CLIENT_TYPE } from "@/config/constant";
|
import { message } from "@/utils";
|
||||||
export interface JsonResult<T> {
|
import { CLIENT_TYPE, LOGIN_ROUTER } from "@/config/constant";
|
||||||
code: number;
|
import { useUserStore } from "@/stores/user";
|
||||||
message: string;
|
|
||||||
data?: T;
|
|
||||||
}
|
|
||||||
|
|
||||||
const axiosConfig: AxiosRequestConfig = {
|
const axiosConfig: AxiosRequestConfig = {
|
||||||
baseURL: import.meta.env.VITE_APP_BASE_API,
|
baseURL: import.meta.env.VITE_APP_BASE_API,
|
||||||
timeout: import.meta.env.VITE_APP_ENV === "production" ? 10000 : 60000,
|
timeout:
|
||||||
|
import.meta.env.VITE_APP_ENV === "production" ? 10 * 1000 : 60 * 1000,
|
||||||
timeoutErrorMessage: "请求超时......",
|
timeoutErrorMessage: "请求超时......",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -27,30 +23,49 @@ class RequestHttp {
|
||||||
this.service = axios.create(config);
|
this.service = axios.create(config);
|
||||||
//1.添加请求拦截
|
//1.添加请求拦截
|
||||||
this.service.interceptors.request.use(
|
this.service.interceptors.request.use(
|
||||||
(config: InternalAxiosRequestConfig) => {
|
(config) => {
|
||||||
//默认带上用户token
|
const userStore = useUserStore();
|
||||||
config.headers.set("token", useUserStore().userInfo?.tokenValue);
|
//请求必须带上当前客户端类型
|
||||||
config.headers.set("Client-Type", CLIENT_TYPE);
|
config.headers.set("Client-Type", CLIENT_TYPE);
|
||||||
|
config.headers.set(
|
||||||
|
userStore.tokenInfo?.tokenName,
|
||||||
|
userStore.tokenInfo?.tokenValue
|
||||||
|
);
|
||||||
|
if (config.loading) {
|
||||||
|
Loading.show(config.loadingMessage);
|
||||||
|
}
|
||||||
|
//todo 默认带上用户token
|
||||||
return config;
|
return config;
|
||||||
},
|
},
|
||||||
(error: AxiosError): Promise<string> => {
|
async (error: AxiosError): Promise<string> => {
|
||||||
message.error(error.message).then((r) => {});
|
Loading.close();
|
||||||
|
message.error(error.message);
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
//2.添加响应拦截
|
//2.添加响应拦截
|
||||||
this.service.interceptors.response.use(
|
this.service.interceptors.response.use(
|
||||||
(response: AxiosResponse): Promise<any> => {
|
async (response: AxiosResponse): Promise<any> => {
|
||||||
|
Loading.close();
|
||||||
const jsonResult: JsonResult<unknown> = response.data;
|
const jsonResult: JsonResult<unknown> = response.data;
|
||||||
if (jsonResult.code !== 200) {
|
if (jsonResult && jsonResult.code !== 200) {
|
||||||
//todo 一些特定的错误需要重新登录 这里暂时没处理
|
//todo 一些特定的错误需要重新登录
|
||||||
message.error(jsonResult.message).then((r) => {});
|
if ([-1].includes(jsonResult.code)) {
|
||||||
|
//清除登录信息
|
||||||
|
useUserStore().resetUserInfo();
|
||||||
|
//跳转登录页
|
||||||
|
await router.push({
|
||||||
|
path: LOGIN_ROUTER.path,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
message.error(jsonResult.message);
|
||||||
return Promise.reject(jsonResult);
|
return Promise.reject(jsonResult);
|
||||||
}
|
}
|
||||||
return Promise.resolve(jsonResult);
|
return Promise.resolve(jsonResult);
|
||||||
},
|
},
|
||||||
(error: AxiosError): Promise<string> => {
|
async (error: AxiosError): Promise<string> => {
|
||||||
message.error(error.message).then((r) => {});
|
Loading.close();
|
||||||
|
message.error(error.message);
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
<template>
|
||||||
|
<n-button
|
||||||
|
@click="toggle"
|
||||||
|
quaternary
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
:name="
|
||||||
|
isFullscreen
|
||||||
|
? 'MaterialSymbolsFullscreenExit'
|
||||||
|
: 'MaterialSymbolsFullscreen'
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</n-button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Icon } from "@/components/icon";
|
||||||
|
import { useFullscreen } from "@vueuse/core";
|
||||||
|
const { isFullscreen, toggle } = useFullscreen();
|
||||||
|
</script>
|
|
@ -1,42 +0,0 @@
|
||||||
<template>
|
|
||||||
<svg :class="svgClass" aria-hidden="true">
|
|
||||||
<use :xlink:href="iconClassName" :fill="color" />
|
|
||||||
</svg>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { computed } from "vue";
|
|
||||||
const props = defineProps({
|
|
||||||
iconName: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
className: {
|
|
||||||
type: String,
|
|
||||||
default: "",
|
|
||||||
},
|
|
||||||
color: {
|
|
||||||
type: String,
|
|
||||||
default: "#ffffff",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
// 图标在 iconfont 中的名字
|
|
||||||
const iconClassName = computed(() => {
|
|
||||||
return `#${props.iconName}`;
|
|
||||||
});
|
|
||||||
// 给图标添加上类名
|
|
||||||
const svgClass = computed(() => {
|
|
||||||
if (props.className) {
|
|
||||||
return `svg-icon ${props.className}`;
|
|
||||||
}
|
|
||||||
return "svg-icon";
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.svg-icon {
|
|
||||||
width: 2em;
|
|
||||||
height: 1.5em;
|
|
||||||
position: relative;
|
|
||||||
fill: currentColor;
|
|
||||||
vertical-align: -2px;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { Base64 } from "js-base64";
|
||||||
|
export const onlinePreview = (url: string) =>
|
||||||
|
"http://172.10.10.238:8012/onlinePreview?url=" +
|
||||||
|
encodeURIComponent(Base64.encode(url));
|
||||||
|
|
||||||
|
import api from "@/axios";
|
||||||
|
interface OBJ {
|
||||||
|
objectUrl: string;
|
||||||
|
preSignedUrl: string;
|
||||||
|
}
|
||||||
|
// export const getPreSignedUrl = async (fileName: string) => {
|
||||||
|
// const resp = await api.get<OBJ>("/file/getPreSignedUrl", {
|
||||||
|
// fileName,
|
||||||
|
// });
|
||||||
|
// return resp.data as OBJ;
|
||||||
|
// };
|
|
@ -0,0 +1,96 @@
|
||||||
|
<template>
|
||||||
|
<n-upload
|
||||||
|
:custom-request="handleCustomRequest"
|
||||||
|
:max="1"
|
||||||
|
:show-file-list="true"
|
||||||
|
accept="*"
|
||||||
|
>
|
||||||
|
<div style="display: flex">
|
||||||
|
<n-button dashed>
|
||||||
|
<Icon name="LineMdUpload" />
|
||||||
|
<span>选择文件上传</span>
|
||||||
|
</n-button>
|
||||||
|
<n-button
|
||||||
|
style="margin-left: 10px"
|
||||||
|
@click.stop="handlePreview"
|
||||||
|
:disabled="!previewURL"
|
||||||
|
>
|
||||||
|
<span>预览文件</span>
|
||||||
|
</n-button>
|
||||||
|
</div>
|
||||||
|
</n-upload>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="tsx">
|
||||||
|
import axios from "@/axios";
|
||||||
|
import { message } from "@/utils/index";
|
||||||
|
import { Icon } from "@/components/icon/index";
|
||||||
|
import { getPreSignedUrl, onlinePreview } from "./index";
|
||||||
|
import { type UploadCustomRequestOptions, useModal } from "naive-ui";
|
||||||
|
const previewURL = defineModel<string>("value", { required: true });
|
||||||
|
const modal = useModal();
|
||||||
|
const fileName = ref("");
|
||||||
|
const show = ref(true);
|
||||||
|
const modalRef = ref();
|
||||||
|
|
||||||
|
const handleCustomRequest = async (options: UploadCustomRequestOptions) => {
|
||||||
|
const { file, onFinish, onError } = options;
|
||||||
|
try {
|
||||||
|
fileName.value = file.name;
|
||||||
|
const { preSignedUrl, objectUrl } = await getPreSignedUrl(fileName.value);
|
||||||
|
const finalURL = __APP_ENV.VITE_APP_MINIO_URL + objectUrl;
|
||||||
|
console.log(
|
||||||
|
"_________________________ ~ handleCustomRequest ~ finalURL:",
|
||||||
|
finalURL
|
||||||
|
);
|
||||||
|
await axios.put(preSignedUrl, file.file as File);
|
||||||
|
onFinish();
|
||||||
|
previewURL.value = finalURL;
|
||||||
|
message.success("上传成功!");
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
onError?.();
|
||||||
|
message.error("上传失败!");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePreview = () => {
|
||||||
|
if (!previewURL.value) {
|
||||||
|
message.warning("没有可预览的文件");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
modalRef.value = modal.create({
|
||||||
|
title: `${fileName.value} 预览`,
|
||||||
|
preset: "card",
|
||||||
|
style: {
|
||||||
|
width: "800px",
|
||||||
|
},
|
||||||
|
content: () => (
|
||||||
|
<n-spin show={show.value}>
|
||||||
|
<iframe
|
||||||
|
src={onlinePreview(previewURL.value)}
|
||||||
|
style={{ width: "100%", height: "50vh" }}
|
||||||
|
onLoad={() => (show.value = false)}
|
||||||
|
/>
|
||||||
|
</n-spin>
|
||||||
|
),
|
||||||
|
footer: () => (
|
||||||
|
<n-space class="flex-end">
|
||||||
|
<n-button
|
||||||
|
type="info"
|
||||||
|
onClick={() =>
|
||||||
|
window.open(onlinePreview(previewURL.value), "_blank")
|
||||||
|
}
|
||||||
|
>
|
||||||
|
新窗口预览
|
||||||
|
</n-button>
|
||||||
|
<n-button onClick={() => modalRef.value?.destroy()}>关闭</n-button>
|
||||||
|
</n-space>
|
||||||
|
),
|
||||||
|
onAfterLeave: () => {
|
||||||
|
modalRef.value?.destroy();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,65 @@
|
||||||
|
<template>
|
||||||
|
<div style="display: flex">
|
||||||
|
<n-image
|
||||||
|
v-if="!!previewURL"
|
||||||
|
height="50"
|
||||||
|
:src="previewURL"
|
||||||
|
/>
|
||||||
|
<n-upload
|
||||||
|
@remove="onRemove"
|
||||||
|
:custom-request="handleCustomRequest"
|
||||||
|
:max="1"
|
||||||
|
:show-file-list="true"
|
||||||
|
accept="*"
|
||||||
|
>
|
||||||
|
<n-button
|
||||||
|
dashed
|
||||||
|
style="margin-right: 5px;"
|
||||||
|
>
|
||||||
|
<Icon name="LineMdUpload" />
|
||||||
|
<span>选择照片上传</span>
|
||||||
|
</n-button>
|
||||||
|
</n-upload>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="tsx">
|
||||||
|
import axios from "@/axios";
|
||||||
|
import { message } from "@/utils/index";
|
||||||
|
import { getPreSignedUrl } from "../customFileUpload/index";
|
||||||
|
import {
|
||||||
|
type UploadCustomRequestOptions,
|
||||||
|
type UploadFileInfo,
|
||||||
|
} from "naive-ui";
|
||||||
|
|
||||||
|
const fileName = ref("");
|
||||||
|
const previewURL = defineModel<string>("value", { required: true });
|
||||||
|
const handleCustomRequest = async (options: UploadCustomRequestOptions) => {
|
||||||
|
const { file, onFinish, onError } = options;
|
||||||
|
try {
|
||||||
|
fileName.value = file.name;
|
||||||
|
const { preSignedUrl, objectUrl } = await getPreSignedUrl(fileName.value);
|
||||||
|
const finalURL = __APP_ENV.VITE_APP_MINIO_URL + objectUrl;
|
||||||
|
console.log(
|
||||||
|
"_________________________ ~ handleCustomRequest ~ finalURL:",
|
||||||
|
finalURL
|
||||||
|
);
|
||||||
|
await axios.put(preSignedUrl, file.file as File);
|
||||||
|
onFinish();
|
||||||
|
previewURL.value = finalURL;
|
||||||
|
message.success("上传成功!");
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
onError?.();
|
||||||
|
message.error("上传失败!");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const onRemove = (options: {
|
||||||
|
file: UploadFileInfo;
|
||||||
|
fileList: Array<UploadFileInfo>;
|
||||||
|
index: number;
|
||||||
|
}): Promise<boolean> | boolean | any => {
|
||||||
|
console.log("onRemove");
|
||||||
|
previewURL.value = "";
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,157 @@
|
||||||
|
<template>
|
||||||
|
<n-form
|
||||||
|
ref="formRef" :model="formData" :inline="true" label-placement="left" class="dynamic-form">
|
||||||
|
<div v-for="(item, index) in formData.items" :key="index">
|
||||||
|
<div style="display: flex">
|
||||||
|
<n-form-item
|
||||||
|
:label="`${userName} (${index + 1})`"
|
||||||
|
>
|
||||||
|
<n-input v-model:value="item.name" clearable />
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item
|
||||||
|
:label="`${userPhone} (${index + 1})`"
|
||||||
|
>
|
||||||
|
<n-input v-model:value="item.phone" clearable />
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item
|
||||||
|
:label="`${userIdCard} (${index + 1})`"
|
||||||
|
>
|
||||||
|
<n-input v-model:value="item.idCard" clearable />
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item>
|
||||||
|
<n-button type="error" secondary @click="removeItem(index)" v-if="index !== 0" :disabled="formData.items.length === 1">
|
||||||
|
{{ removeButtonText }}
|
||||||
|
</n-button>
|
||||||
|
</n-form-item>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<n-button type="primary" @click="addItem" class="add-btn">
|
||||||
|
{{ addButtonText }}
|
||||||
|
</n-button>
|
||||||
|
</n-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, reactive, watch } from "vue";
|
||||||
|
import type { FormInst, FormRules } from "naive-ui";
|
||||||
|
interface FormItem {
|
||||||
|
name: string;
|
||||||
|
phone: string;
|
||||||
|
idCard: string;
|
||||||
|
}
|
||||||
|
const props = withDefaults(defineProps<{
|
||||||
|
modelValue: FormItem[]
|
||||||
|
addButtonText?: string
|
||||||
|
removeButtonText?: string
|
||||||
|
userName:string,
|
||||||
|
userPhone:'',
|
||||||
|
userIdCard:''
|
||||||
|
}>(), {
|
||||||
|
modelValue: () => [{
|
||||||
|
name: "",
|
||||||
|
phone: "",
|
||||||
|
idCard: ""
|
||||||
|
}],
|
||||||
|
addButtonText: '添加',
|
||||||
|
removeButtonText: '删除',
|
||||||
|
userName:'',
|
||||||
|
userPhone:'',
|
||||||
|
userIdCard:''
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
'update:modelValue': [value: FormItem[]];
|
||||||
|
'add': [index: number];
|
||||||
|
'remove': [index: number];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
// 表单验证规则
|
||||||
|
const rules: FormRules = {
|
||||||
|
name: {
|
||||||
|
required: true,
|
||||||
|
message: `${props.userName}不能为空`,
|
||||||
|
trigger: [ 'input']
|
||||||
|
},
|
||||||
|
phone: {
|
||||||
|
required: true,
|
||||||
|
validator: (_, value: string) => {
|
||||||
|
if (!/^1[3-9]\d{9}$/.test(value)) {
|
||||||
|
return new Error("请输入有效的手机号码");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
trigger: [ 'input']
|
||||||
|
},
|
||||||
|
idCard: {
|
||||||
|
required: true,
|
||||||
|
validator: (_, value: string) => {
|
||||||
|
if (!/(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/.test(value)) {
|
||||||
|
return new Error("请输入有效的身份证号码");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
trigger: [ 'input']
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 表单数据
|
||||||
|
const formRef = ref<FormInst | null>(null);
|
||||||
|
|
||||||
|
const formData = reactive<{ items: FormItem[] }>({
|
||||||
|
items: [...props.modelValue]
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听数据变化
|
||||||
|
watch(() => props.modelValue, (newVal) => {
|
||||||
|
if (JSON.stringify(newVal) !== JSON.stringify(formData.items)) {
|
||||||
|
formData.items = [...newVal];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 添加条目
|
||||||
|
const addItem = () => {
|
||||||
|
formData.items.push({
|
||||||
|
name: "",
|
||||||
|
phone: "",
|
||||||
|
idCard: ""
|
||||||
|
});
|
||||||
|
emit('update:modelValue', formData.items);
|
||||||
|
emit('add', formData.items.length - 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 删除条目
|
||||||
|
const removeItem = (index: number) => {
|
||||||
|
if (formData.items.length > 1) {
|
||||||
|
formData.items.splice(index, 1);
|
||||||
|
emit('update:modelValue', formData.items);
|
||||||
|
emit('remove', index);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 表单验证
|
||||||
|
const validate = () => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
formRef.value?.validate((errors) => {
|
||||||
|
if (!errors) {
|
||||||
|
resolve(formData.items);
|
||||||
|
} else {
|
||||||
|
reject(errors);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 暴露验证方法
|
||||||
|
defineExpose({ validate });
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.dynamic-form {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
width: 100%;
|
||||||
|
.add-btn{
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,54 +1,30 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="not-container">
|
<div
|
||||||
<img src="@/assets/images/404.png" class="not-img" alt="404" />
|
style="
|
||||||
<div class="not-detail">
|
height: 100vh;
|
||||||
<h2>404</h2>
|
width: 100vw;
|
||||||
<h4>抱歉,您访问的页面不存在~🤷♂️🤷♀️</h4>
|
display: flex;
|
||||||
<!-- <a-button type="primary" @click="router.push(INDEX_URL)"
|
flex-direction: column;
|
||||||
>返回首页</a-button -->
|
justify-content: center;
|
||||||
</div>
|
align-items: center;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<n-result
|
||||||
|
status="404"
|
||||||
|
title="404 资源不存在"
|
||||||
|
description="生活总归带点荒谬"
|
||||||
|
>
|
||||||
|
<template #footer>
|
||||||
|
<n-button @click.prevent="toHome">返回主页</n-button>
|
||||||
|
</template>
|
||||||
|
</n-result>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
|
const router = useRouter();
|
||||||
const router = useRouter();
|
function toHome() {
|
||||||
|
router.push("/");
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.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: red;
|
|
||||||
}
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
margin: 30px 0 20px;
|
|
||||||
font-size: 19px;
|
|
||||||
font-weight: normal;
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
<script setup lang="ts"></script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div>500</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
|
@ -0,0 +1,335 @@
|
||||||
|
// import {
|
||||||
|
// type Component,
|
||||||
|
// type PropType,
|
||||||
|
// type VNode,
|
||||||
|
// computed,
|
||||||
|
// defineComponent,
|
||||||
|
// h,
|
||||||
|
// ref,
|
||||||
|
// } from "vue";
|
||||||
|
// import { formProps } from "naive-ui/es/form/src/Form";
|
||||||
|
// import type { FormInst, LabelPlacement } from "naive-ui/es/form/src/interface";
|
||||||
|
// import {
|
||||||
|
// NCascader,
|
||||||
|
// NCheckbox,
|
||||||
|
// NCheckboxGroup,
|
||||||
|
// NDatePicker,
|
||||||
|
// NForm,
|
||||||
|
// NFormItemGi,
|
||||||
|
// NGrid,
|
||||||
|
// NInput,
|
||||||
|
// NInputNumber,
|
||||||
|
// NRadio,
|
||||||
|
// NRadioGroup,
|
||||||
|
// NSelect,
|
||||||
|
// NSpace,
|
||||||
|
// NTimePicker,
|
||||||
|
// NTreeSelect,
|
||||||
|
// type GridProps,
|
||||||
|
// } from "naive-ui";
|
||||||
|
// import type {
|
||||||
|
// Fm,
|
||||||
|
// FormItemOptions,
|
||||||
|
// FormModelValue,
|
||||||
|
// FormProInst,
|
||||||
|
// FormProItemProps,
|
||||||
|
// } from "../src/indexface";
|
||||||
|
|
||||||
|
// export const formProProps = {
|
||||||
|
// ...formProps,
|
||||||
|
// labelWidth: {
|
||||||
|
// type: [Number, String] as PropType<number | string>,
|
||||||
|
// },
|
||||||
|
// labelPlacement: {
|
||||||
|
// type: String as PropType<LabelPlacement>,
|
||||||
|
// default: "left",
|
||||||
|
// },
|
||||||
|
// value: {
|
||||||
|
// type: Object as PropType<any>,
|
||||||
|
// },
|
||||||
|
// gridProps: {
|
||||||
|
// type: Object as PropType<GridProps>,
|
||||||
|
// default: {
|
||||||
|
// cols: 1,
|
||||||
|
// xGap: 8,
|
||||||
|
// itemResponsive: true,
|
||||||
|
// responsive: "self",
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// formItemOptions: {
|
||||||
|
// type: Object as PropType<FormItemOptions<any>>,
|
||||||
|
// },
|
||||||
|
// };
|
||||||
|
|
||||||
|
// export default defineComponent({
|
||||||
|
// name: "FormPro",
|
||||||
|
// props: formProProps,
|
||||||
|
// emits: ["update:value"],
|
||||||
|
// setup(props, { emit, expose }) {
|
||||||
|
// //表单实例
|
||||||
|
// const formRef = ref<FormInst>();
|
||||||
|
// //表单值
|
||||||
|
// const formModelValue = ref<FormModelValue>(props.value || {});
|
||||||
|
|
||||||
|
// //初始化一些配置属性
|
||||||
|
// const formOptions = computed<any>(() => {
|
||||||
|
// if (!props.formItemOptions) {
|
||||||
|
// return {};
|
||||||
|
// }
|
||||||
|
// return Object.fromEntries(
|
||||||
|
// Object.entries(props.formItemOptions).map(([key, value]) => {
|
||||||
|
// const item = { ...value };
|
||||||
|
// item.path = key;
|
||||||
|
// //设置默认校验规则
|
||||||
|
// item.required &&
|
||||||
|
// (item.rule = { required: true, message: `${item.label}不能为空` });
|
||||||
|
// //初始化组件配置
|
||||||
|
// !item.componentsProps && (item.componentsProps = {});
|
||||||
|
// //设置提示词
|
||||||
|
// !item.componentsProps.placeholder &&
|
||||||
|
// (item.componentsProps.placeholder = item.type.includes("input")
|
||||||
|
// ? `请输入${item.label}`
|
||||||
|
// : `请选择${item.label}`);
|
||||||
|
// //默认可清除
|
||||||
|
// item.componentsProps?.clearable ??
|
||||||
|
// (item.componentsProps.clearable = true);
|
||||||
|
// return [key, item];
|
||||||
|
// })
|
||||||
|
// );
|
||||||
|
// });
|
||||||
|
|
||||||
|
// expose<FormProInst<any>>({
|
||||||
|
// validate: () => formRef.value!.validate(),
|
||||||
|
// restoreValidation: () => formRef.value!.restoreValidation(),
|
||||||
|
// });
|
||||||
|
// return () => (
|
||||||
|
// <>
|
||||||
|
// <NForm ref={formRef} {...props} model={formModelValue.value}>
|
||||||
|
// <NGrid {...props.gridProps}>
|
||||||
|
// {Object.entries(formOptions.value).map(([field, item]) => {
|
||||||
|
// return (
|
||||||
|
// <NFormItemGi {...item} key={field}>
|
||||||
|
// {comps[item.type](formModelValue.value, field, item)}
|
||||||
|
// </NFormItemGi>
|
||||||
|
// );
|
||||||
|
// })}
|
||||||
|
// </NGrid>
|
||||||
|
// </NForm>
|
||||||
|
// </>
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
|
||||||
|
// const comps: Record<
|
||||||
|
// keyof Fm,
|
||||||
|
// (
|
||||||
|
// modelValue: { [k in string]: any },
|
||||||
|
// field: string,
|
||||||
|
// itemProps: FormProItemProps<any, any>
|
||||||
|
// ) => VNode
|
||||||
|
// > = {
|
||||||
|
// custom: (modelValue, _, itemProps: FormProItemProps<any, "custom">) => {
|
||||||
|
// if (itemProps.customRender) {
|
||||||
|
// return itemProps.customRender(modelValue);
|
||||||
|
// } else {
|
||||||
|
// return h(itemProps.defineComponent as Component, {
|
||||||
|
// modelValue,
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// input: (modelValue, field, itemProps: FormProItemProps<any, "input">) => {
|
||||||
|
// return h(
|
||||||
|
// NInput,
|
||||||
|
// {
|
||||||
|
// style: {
|
||||||
|
// width: "100%",
|
||||||
|
// },
|
||||||
|
// ...itemProps.componentsProps,
|
||||||
|
// value: modelValue[field],
|
||||||
|
// "onUpdate:value": (val) => (modelValue[field] = val),
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// ...itemProps.componentsSlots,
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// inputNumber: (
|
||||||
|
// modelValue,
|
||||||
|
// field,
|
||||||
|
// itemProps: FormProItemProps<any, "inputNumber">
|
||||||
|
// ) => {
|
||||||
|
// return h(
|
||||||
|
// NInputNumber,
|
||||||
|
// {
|
||||||
|
// style: {
|
||||||
|
// width: "100%",
|
||||||
|
// },
|
||||||
|
// ...itemProps.componentsProps,
|
||||||
|
// value: modelValue[field],
|
||||||
|
// "onUpdate:value": (val) => (modelValue[field] = val),
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// ...itemProps.componentsSlots,
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// radioGroup: (
|
||||||
|
// modelValue,
|
||||||
|
// field,
|
||||||
|
// itemProps: FormProItemProps<any, "radioGroup">
|
||||||
|
// ) => {
|
||||||
|
// return h(
|
||||||
|
// NRadioGroup,
|
||||||
|
// {
|
||||||
|
// style: {
|
||||||
|
// width: "100%",
|
||||||
|
// },
|
||||||
|
// ...itemProps.componentsProps,
|
||||||
|
// value: modelValue[field],
|
||||||
|
// "onUpdate:value": (val) => (modelValue[field] = val),
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// default: () => (
|
||||||
|
// <NSpace>
|
||||||
|
// {itemProps.options?.map((o) => {
|
||||||
|
// return (
|
||||||
|
// //@ts-ignore 这里类型检查会有问题 其实没有问题
|
||||||
|
// <NRadio key={o.value} value={o.value}>
|
||||||
|
// {o.label}
|
||||||
|
// </NRadio>
|
||||||
|
// );
|
||||||
|
// })}
|
||||||
|
// </NSpace>
|
||||||
|
// ),
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// checkboxGroup: (
|
||||||
|
// modelValue,
|
||||||
|
// field,
|
||||||
|
// itemProps: FormProItemProps<any, "checkboxGroup">
|
||||||
|
// ) => {
|
||||||
|
// return h(
|
||||||
|
// NCheckboxGroup,
|
||||||
|
// {
|
||||||
|
// style: {
|
||||||
|
// width: "100%",
|
||||||
|
// },
|
||||||
|
// ...itemProps.componentsProps,
|
||||||
|
// value: modelValue[field],
|
||||||
|
// "onUpdate:value": (val) => (modelValue[field] = val),
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// default: () => {
|
||||||
|
// return (
|
||||||
|
// <NSpace>
|
||||||
|
// {itemProps.options?.map((o) => {
|
||||||
|
// return (
|
||||||
|
// //@ts-ignore 这里类型检查会有问题 其实没有问题
|
||||||
|
// <NCheckbox key={o.value} value={o.value} label={o.label} />
|
||||||
|
// );
|
||||||
|
// })}
|
||||||
|
// </NSpace>
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// ...itemProps.componentsSlots,
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// select: (modelValue, field, itemProps: FormProItemProps<any, "select">) => {
|
||||||
|
// return h(
|
||||||
|
// NSelect,
|
||||||
|
// {
|
||||||
|
// style: {
|
||||||
|
// width: "100%",
|
||||||
|
// },
|
||||||
|
// ...itemProps.componentsProps,
|
||||||
|
// value: modelValue[field],
|
||||||
|
// "onUpdate:value": (val) => (modelValue[field] = val),
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// ...itemProps.componentsSlots,
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// treeSelect: (
|
||||||
|
// modelValue,
|
||||||
|
// field,
|
||||||
|
// itemProps: FormProItemProps<any, "treeSelect">
|
||||||
|
// ) => {
|
||||||
|
// return h(
|
||||||
|
// NTreeSelect,
|
||||||
|
// {
|
||||||
|
// style: {
|
||||||
|
// width: "100%",
|
||||||
|
// },
|
||||||
|
// ...itemProps.componentsProps,
|
||||||
|
// value: modelValue[field],
|
||||||
|
// "onUpdate:value": (val) => (modelValue[field] = val),
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// ...itemProps.componentsSlots,
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// cascader: (
|
||||||
|
// modelValue,
|
||||||
|
// field,
|
||||||
|
// itemProps: FormProItemProps<any, "cascader">
|
||||||
|
// ) => {
|
||||||
|
// return h(
|
||||||
|
// NCascader,
|
||||||
|
// {
|
||||||
|
// style: {
|
||||||
|
// width: "100%",
|
||||||
|
// },
|
||||||
|
// ...itemProps.componentsProps,
|
||||||
|
// value: modelValue[field],
|
||||||
|
// "onUpdate:value": (val) => (modelValue[field] = val),
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// ...itemProps.componentsSlots,
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// datePicker: (
|
||||||
|
// modelValue,
|
||||||
|
// field,
|
||||||
|
// itemProps: FormProItemProps<any, "datePicker">
|
||||||
|
// ) => {
|
||||||
|
// return h(
|
||||||
|
// NDatePicker,
|
||||||
|
// {
|
||||||
|
// style: {
|
||||||
|
// width: "100%",
|
||||||
|
// },
|
||||||
|
// ...itemProps.componentsProps,
|
||||||
|
// value: modelValue[field],
|
||||||
|
// "onUpdate:value": (val) => (modelValue[field] = val),
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// ...itemProps.componentsSlots,
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// timePicker: (
|
||||||
|
// modelValue,
|
||||||
|
// field,
|
||||||
|
// itemProps: FormProItemProps<any, "timePicker">
|
||||||
|
// ) => {
|
||||||
|
// return h(
|
||||||
|
// NTimePicker,
|
||||||
|
// {
|
||||||
|
// style: {
|
||||||
|
// width: "100%",
|
||||||
|
// },
|
||||||
|
// ...itemProps.componentsProps,
|
||||||
|
// value: modelValue[field],
|
||||||
|
// "onUpdate:value": (val) => (modelValue[field] = val),
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// ...itemProps.componentsSlots,
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// };
|
|
@ -0,0 +1,111 @@
|
||||||
|
<template>
|
||||||
|
<FormPro ref="form" v-model:value="modelValue" :form-item-options="formOptions" />
|
||||||
|
{{ modelValue }}
|
||||||
|
</template>
|
||||||
|
<script setup lang="tsx">
|
||||||
|
import { defineComponent, markRaw, reactive, useTemplateRef } from 'vue'
|
||||||
|
import { FormPro, type FormItemOptions, type FormProInst } from '@/components'
|
||||||
|
|
||||||
|
const formRef = useTemplateRef<FormProInst>('form')
|
||||||
|
|
||||||
|
interface A {
|
||||||
|
name: string
|
||||||
|
age: number
|
||||||
|
info: string
|
||||||
|
text: string
|
||||||
|
text2: string
|
||||||
|
like: string
|
||||||
|
aaa: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const modelValue = reactive<A>({
|
||||||
|
name: 'zs',
|
||||||
|
age: 12,
|
||||||
|
info: 'asdsadas',
|
||||||
|
text: '222',
|
||||||
|
text2: '',
|
||||||
|
like: '',
|
||||||
|
aaa: [],
|
||||||
|
})
|
||||||
|
|
||||||
|
const formOptions = reactive<FormItemOptions<A>>({
|
||||||
|
name: {
|
||||||
|
type: 'input',
|
||||||
|
label: '名字',
|
||||||
|
componentsProps: {
|
||||||
|
disabled: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
like: {
|
||||||
|
type: 'radioGroup',
|
||||||
|
label: '爱好',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
value: 0,
|
||||||
|
label: '唱',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 1,
|
||||||
|
label: '跳',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 2,
|
||||||
|
label: 'rap',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
aaa: {
|
||||||
|
type: 'checkboxGroup',
|
||||||
|
label: 'aaa',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
value: 0,
|
||||||
|
label: '唱',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 1,
|
||||||
|
label: '跳',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 2,
|
||||||
|
label: 'rap',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
age: {
|
||||||
|
type: 'inputNumber',
|
||||||
|
label: '年龄',
|
||||||
|
},
|
||||||
|
info: {
|
||||||
|
type: 'input',
|
||||||
|
label: '介绍',
|
||||||
|
componentsProps: {},
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
type: 'custom',
|
||||||
|
label: '文件',
|
||||||
|
customRender: (modelValue) => {
|
||||||
|
return <n-input v-model:value={modelValue.text}></n-input>
|
||||||
|
},
|
||||||
|
},
|
||||||
|
text2: {
|
||||||
|
type: 'custom',
|
||||||
|
label: '文件',
|
||||||
|
defineComponent: markRaw(
|
||||||
|
defineComponent(
|
||||||
|
({ modelValue }) => {
|
||||||
|
|
||||||
|
return () => <n-input v-model:value={modelValue.text2}></n-input>
|
||||||
|
},
|
||||||
|
{ props: ['modelValue'] }
|
||||||
|
)
|
||||||
|
),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
formOptions.name.componentsProps.disabled = true
|
||||||
|
}, 3000)
|
||||||
|
</script>
|
||||||
|
<style scoped></style>
|
|
@ -0,0 +1,3 @@
|
||||||
|
export { formProProps } from './src/FormPro'
|
||||||
|
export type { FormModelValue, FormProProps, FormItemOptions, FormProItemProps, FormProInst } from './src/indexface'
|
||||||
|
export { default as FormPro } from './src/FormPro.vue'
|
|
@ -0,0 +1,51 @@
|
||||||
|
import { type PropType } from "vue";
|
||||||
|
import { formProps } from "naive-ui/es/form/src/Form";
|
||||||
|
import type {
|
||||||
|
FormItemRuleValidator,
|
||||||
|
LabelAlign,
|
||||||
|
LabelPlacement,
|
||||||
|
} from "naive-ui/es/form/src/interface";
|
||||||
|
import type { FormItemOptions } from "./indexface";
|
||||||
|
import { type FormItemProps, type GridProps } from "naive-ui";
|
||||||
|
import { isEmpty } from "lodash-es";
|
||||||
|
|
||||||
|
export const formProProps = {
|
||||||
|
...formProps,
|
||||||
|
labelWidth: {
|
||||||
|
type: [Number, String] as PropType<number | string>,
|
||||||
|
},
|
||||||
|
labelPlacement: {
|
||||||
|
type: String as PropType<LabelPlacement>,
|
||||||
|
default: "left",
|
||||||
|
},
|
||||||
|
labelAlign: {
|
||||||
|
type: String as PropType<LabelAlign>,
|
||||||
|
default: "left",
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: Object as PropType<any>,
|
||||||
|
},
|
||||||
|
gridProps: {
|
||||||
|
type: Object as PropType<GridProps>,
|
||||||
|
default: {
|
||||||
|
cols: 1,
|
||||||
|
xGap: 8,
|
||||||
|
itemResponsive: true,
|
||||||
|
responsive: "self",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
formItemOptions: {
|
||||||
|
type: Object as PropType<FormItemOptions<any>>,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// 定义通用校验规则
|
||||||
|
export const universalValidator: FormItemRuleValidator = (_, value) => {
|
||||||
|
if (value === null || value === undefined) {
|
||||||
|
return false;
|
||||||
|
} else if (typeof value === "string" && value.trim() === "") {
|
||||||
|
return false;
|
||||||
|
} else if (Array.isArray(value) && value.length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,97 @@
|
||||||
|
<template>
|
||||||
|
<n-form
|
||||||
|
ref="formInst"
|
||||||
|
v-bind="props"
|
||||||
|
:model="formModelValue"
|
||||||
|
>
|
||||||
|
<n-grid v-bind="props.gridProps">
|
||||||
|
<n-form-item-gi
|
||||||
|
v-for="(item, field) in formOptions"
|
||||||
|
:key="field"
|
||||||
|
v-bind="item"
|
||||||
|
>
|
||||||
|
<template #label>
|
||||||
|
<label style="display: inline-flex; align-items: center">
|
||||||
|
<span>
|
||||||
|
{{ item.label }}
|
||||||
|
</span>
|
||||||
|
<template v-if="item.remarkRender">
|
||||||
|
<n-popover trigger="hover">
|
||||||
|
<template #trigger>
|
||||||
|
<Icon
|
||||||
|
name="LucideCircleHelp"
|
||||||
|
color="#ff4a36"
|
||||||
|
:size="18"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #default>
|
||||||
|
<component :is="item.remarkRender" />
|
||||||
|
</template>
|
||||||
|
</n-popover>
|
||||||
|
</template>
|
||||||
|
</label>
|
||||||
|
</template>
|
||||||
|
<component
|
||||||
|
:is="FormSupportComp[item.type](formModelValue, field, item)"
|
||||||
|
/>
|
||||||
|
</n-form-item-gi>
|
||||||
|
</n-grid>
|
||||||
|
</n-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" generic="T extends FormModelValue">
|
||||||
|
import { computed, useTemplateRef } from "vue";
|
||||||
|
import { formProProps, universalValidator } from "./FormPro";
|
||||||
|
import type { FormModelValue, FormProInst } from "./indexface";
|
||||||
|
import type { FormInst } from "naive-ui";
|
||||||
|
import FormSupportComp from "./FormSupportComp";
|
||||||
|
import { cloneDeep } from "lodash-es";
|
||||||
|
import { Icon } from "@/components/icon";
|
||||||
|
|
||||||
|
//表单实例
|
||||||
|
const formRef = useTemplateRef<FormInst>("formInst");
|
||||||
|
|
||||||
|
const props = defineProps(formProProps);
|
||||||
|
|
||||||
|
const formModelValue = defineModel<T>("value", { default: {} });
|
||||||
|
|
||||||
|
//初始化一些配置属性
|
||||||
|
const formOptions = computed(() => {
|
||||||
|
if (!props.formItemOptions) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return Object.fromEntries(
|
||||||
|
Object.entries(props.formItemOptions).map(([key, value]) => {
|
||||||
|
const item = cloneDeep(value);
|
||||||
|
item.path = key;
|
||||||
|
//设置默认校验规则 rule优先级更高
|
||||||
|
item.required &&
|
||||||
|
!item.rule &&
|
||||||
|
(item.rule = {
|
||||||
|
required: true,
|
||||||
|
message: `${item.label}不能为空`,
|
||||||
|
validator: universalValidator,
|
||||||
|
trigger: "blur",
|
||||||
|
});
|
||||||
|
//初始化组件配置
|
||||||
|
!item.componentsProps && (item.componentsProps = {});
|
||||||
|
//设置提示词
|
||||||
|
!item.componentsProps.placeholder &&
|
||||||
|
(item.componentsProps.placeholder = item.type.includes("input")
|
||||||
|
? `请输入${item.label}`
|
||||||
|
: `请选择${item.label}`);
|
||||||
|
//默认可清除
|
||||||
|
item.componentsProps?.clearable ??
|
||||||
|
(item.componentsProps.clearable = true);
|
||||||
|
return [key, item];
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
defineExpose<FormProInst>({
|
||||||
|
validate: () => formRef.value!.validate(),
|
||||||
|
restoreValidation: () => formRef.value!.restoreValidation(),
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
|
@ -0,0 +1,194 @@
|
||||||
|
import { type DefineComponent, type VNode } from "vue";
|
||||||
|
import type { Fm, FormProItemProps } from "./indexface";
|
||||||
|
import {
|
||||||
|
NCascader,
|
||||||
|
NCheckbox,
|
||||||
|
NCheckboxGroup,
|
||||||
|
NDatePicker,
|
||||||
|
NInput,
|
||||||
|
NInputNumber,
|
||||||
|
NRadio,
|
||||||
|
NRadioGroup,
|
||||||
|
NSelect,
|
||||||
|
NSpace,
|
||||||
|
NTimePicker,
|
||||||
|
NTreeSelect,
|
||||||
|
} from "naive-ui";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
custom: (modelValue, _, itemProps: FormProItemProps<any, "custom">) => {
|
||||||
|
if (itemProps.customRender) {
|
||||||
|
return itemProps.customRender(modelValue);
|
||||||
|
} else {
|
||||||
|
const Df = itemProps.defineComponent as DefineComponent<{
|
||||||
|
modelValue: typeof modelValue;
|
||||||
|
}>;
|
||||||
|
return <Df modelValue={modelValue} />;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
input: (modelValue, field, itemProps: FormProItemProps<any, "input">) => {
|
||||||
|
return (
|
||||||
|
<NInput
|
||||||
|
style={"width:100%"}
|
||||||
|
v-model:value={modelValue[field]}
|
||||||
|
{...itemProps.componentsProps}
|
||||||
|
v-slots={{ ...itemProps.componentsSlots }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
inputNumber: (
|
||||||
|
modelValue,
|
||||||
|
field,
|
||||||
|
itemProps: FormProItemProps<any, "inputNumber">
|
||||||
|
) => {
|
||||||
|
return (
|
||||||
|
<NInputNumber
|
||||||
|
style={"width:100%"}
|
||||||
|
v-model:value={modelValue[field]}
|
||||||
|
{...itemProps.componentsProps}
|
||||||
|
v-slots={{ ...itemProps.componentsSlots }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
radioGroup: (
|
||||||
|
modelValue,
|
||||||
|
field,
|
||||||
|
itemProps: FormProItemProps<any, "radioGroup">
|
||||||
|
) => {
|
||||||
|
return (
|
||||||
|
<NRadioGroup
|
||||||
|
style={"width:100%"}
|
||||||
|
v-model:value={modelValue[field]}
|
||||||
|
{...itemProps.componentsProps}
|
||||||
|
v-slots={{
|
||||||
|
default: () => (
|
||||||
|
<NSpace>
|
||||||
|
{(itemProps.options as SelectNodeVo<any>[])?.map((o) => {
|
||||||
|
return (
|
||||||
|
<NRadio
|
||||||
|
key={o.value}
|
||||||
|
value={o.value}
|
||||||
|
>
|
||||||
|
{o.label}
|
||||||
|
</NRadio>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</NSpace>
|
||||||
|
),
|
||||||
|
...itemProps.componentsSlots,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
checkboxGroup: (
|
||||||
|
modelValue,
|
||||||
|
field,
|
||||||
|
itemProps: FormProItemProps<any, "checkboxGroup">
|
||||||
|
) => {
|
||||||
|
return (
|
||||||
|
<NCheckboxGroup
|
||||||
|
style={"width:100%"}
|
||||||
|
v-model:value={modelValue[field]}
|
||||||
|
{...itemProps.componentsProps}
|
||||||
|
v-slots={{
|
||||||
|
default: () => (
|
||||||
|
<NSpace>
|
||||||
|
{(itemProps.options as SelectNodeVo<any>[])?.map((o) => {
|
||||||
|
return (
|
||||||
|
<NCheckbox
|
||||||
|
key={o.value}
|
||||||
|
value={o.value}
|
||||||
|
label={o.label}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</NSpace>
|
||||||
|
),
|
||||||
|
...itemProps.componentsSlots,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
select: (modelValue, field, itemProps: FormProItemProps<any, "select">) => {
|
||||||
|
return (
|
||||||
|
<NSelect
|
||||||
|
style={"width:100%"}
|
||||||
|
v-model:value={modelValue[field]}
|
||||||
|
options={itemProps.options as any[]}
|
||||||
|
labelField="label"
|
||||||
|
valueField="value"
|
||||||
|
childrenField="children"
|
||||||
|
{...itemProps.componentsProps}
|
||||||
|
v-slots={{ ...itemProps.componentsSlots }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
treeSelect: (
|
||||||
|
modelValue,
|
||||||
|
field,
|
||||||
|
itemProps: FormProItemProps<any, "treeSelect">
|
||||||
|
) => {
|
||||||
|
return (
|
||||||
|
<NTreeSelect
|
||||||
|
style={"width:100%"}
|
||||||
|
v-model:value={modelValue[field]}
|
||||||
|
options={itemProps.options as any[]}
|
||||||
|
labelField="label"
|
||||||
|
keyField="value"
|
||||||
|
childrenField="children"
|
||||||
|
{...itemProps.componentsProps}
|
||||||
|
v-slots={{ ...itemProps.componentsSlots }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
cascader: (
|
||||||
|
modelValue,
|
||||||
|
field,
|
||||||
|
itemProps: FormProItemProps<any, "cascader">
|
||||||
|
) => {
|
||||||
|
return (
|
||||||
|
<NCascader
|
||||||
|
style={"width:100%"}
|
||||||
|
v-model:value={modelValue[field]}
|
||||||
|
options={itemProps.options as any[]}
|
||||||
|
{...itemProps.componentsProps}
|
||||||
|
v-slots={{ ...itemProps.componentsSlots }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
datePicker: (
|
||||||
|
modelValue,
|
||||||
|
field,
|
||||||
|
itemProps: FormProItemProps<any, "datePicker">
|
||||||
|
) => {
|
||||||
|
return (
|
||||||
|
<NDatePicker
|
||||||
|
style={"width:100%"}
|
||||||
|
v-model:value={modelValue[field]}
|
||||||
|
{...itemProps.componentsProps}
|
||||||
|
v-slots={{ ...itemProps.componentsSlots }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
timePicker: (
|
||||||
|
modelValue,
|
||||||
|
field,
|
||||||
|
itemProps: FormProItemProps<any, "timePicker">
|
||||||
|
) => {
|
||||||
|
return (
|
||||||
|
<NTimePicker
|
||||||
|
style={"width:100%"}
|
||||||
|
v-model:value={modelValue[field]}
|
||||||
|
{...itemProps.componentsProps}
|
||||||
|
v-slots={{ ...itemProps.componentsSlots }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
} as Record<
|
||||||
|
keyof Fm,
|
||||||
|
(
|
||||||
|
modelValue: { [k in string]: any },
|
||||||
|
field: any,
|
||||||
|
itemProps: FormProItemProps<any, any>
|
||||||
|
) => VNode
|
||||||
|
>;
|
|
@ -0,0 +1,118 @@
|
||||||
|
import type {
|
||||||
|
CascaderProps,
|
||||||
|
CascaderSlots,
|
||||||
|
CheckboxGroupProps,
|
||||||
|
DatePickerProps,
|
||||||
|
DatePickerSlots,
|
||||||
|
FormInst,
|
||||||
|
FormItemGiProps,
|
||||||
|
FormProps,
|
||||||
|
GridProps,
|
||||||
|
InputNumberProps,
|
||||||
|
InputNumberSlots,
|
||||||
|
InputProps,
|
||||||
|
InputSlots,
|
||||||
|
RadioGroupProps,
|
||||||
|
SelectProps,
|
||||||
|
SelectSlots,
|
||||||
|
TimePickerProps,
|
||||||
|
TimePickerSlots,
|
||||||
|
TreeSelectProps,
|
||||||
|
TreeSelectSlots,
|
||||||
|
} from "naive-ui";
|
||||||
|
import type { Component, Ref, VNode } from "vue";
|
||||||
|
import type { JSX } from "vue/jsx-runtime";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表单对象
|
||||||
|
*/
|
||||||
|
export type FormModelValue = { [k in string]: any };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组件默认插槽
|
||||||
|
*/
|
||||||
|
type DefaultSlots = {
|
||||||
|
default: () => any;
|
||||||
|
};
|
||||||
|
|
||||||
|
type CompPropsAndSlots<P, S> = {
|
||||||
|
props: P;
|
||||||
|
slots: S;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支持的组件列表
|
||||||
|
*/
|
||||||
|
export type Fm = {
|
||||||
|
custom: CompPropsAndSlots<any, any>;
|
||||||
|
input: CompPropsAndSlots<InputProps, InputSlots>;
|
||||||
|
inputNumber: CompPropsAndSlots<InputNumberProps, InputNumberSlots>;
|
||||||
|
radioGroup: CompPropsAndSlots<RadioGroupProps, DefaultSlots>;
|
||||||
|
checkboxGroup: CompPropsAndSlots<CheckboxGroupProps, DefaultSlots>;
|
||||||
|
select: CompPropsAndSlots<SelectProps, SelectSlots>;
|
||||||
|
treeSelect: CompPropsAndSlots<TreeSelectProps, TreeSelectSlots>;
|
||||||
|
cascader: CompPropsAndSlots<CascaderProps, CascaderSlots>;
|
||||||
|
datePicker: CompPropsAndSlots<DatePickerProps, DatePickerSlots>;
|
||||||
|
timePicker: CompPropsAndSlots<TimePickerProps, TimePickerSlots>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选项
|
||||||
|
*/
|
||||||
|
type OptionType =
|
||||||
|
| (SelectNodeVo<any> | TreeNodeVo<any>)[]
|
||||||
|
| Ref<SelectNodeVo<any>[] | TreeNodeVo<any>[]>;
|
||||||
|
|
||||||
|
type BaseFormProItemProps<ModelValue> = FormItemGiProps & {
|
||||||
|
/**
|
||||||
|
* 描述说明
|
||||||
|
*/
|
||||||
|
remarkRender?: JSX.Element;
|
||||||
|
/**
|
||||||
|
* 配置项
|
||||||
|
*/
|
||||||
|
options?: OptionType;
|
||||||
|
/**
|
||||||
|
* 当type为custom时 render函数跟defineComponent 只应该存在一个 render函数无状态 defineComponent有状态
|
||||||
|
* @param modelValue 回传表单对象
|
||||||
|
*/
|
||||||
|
customRender?: (modelValue: ModelValue) => VNode;
|
||||||
|
defineComponent?: Component<{ modelValue: ModelValue }>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表单项配置
|
||||||
|
*/
|
||||||
|
export type FormProItemProps<
|
||||||
|
ModelValue,
|
||||||
|
TK extends keyof Fm
|
||||||
|
> = BaseFormProItemProps<ModelValue> & {
|
||||||
|
type: TK;
|
||||||
|
componentsProps?: Fm[TK]["props"];
|
||||||
|
componentsSlots?: Fm[TK]["slots"];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表单组件配置
|
||||||
|
*/
|
||||||
|
export type FormItemOptions<ModelValue> = {
|
||||||
|
[key in keyof ModelValue]: {
|
||||||
|
[T in keyof Fm]: FormProItemProps<ModelValue, T>;
|
||||||
|
}[keyof Fm];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表单配置
|
||||||
|
*/
|
||||||
|
export type FormProProps<ModalValue = {}> = FormProps & {
|
||||||
|
value: FormModelValue;
|
||||||
|
"onUpdate:value": Function;
|
||||||
|
onUpdateValue: Function;
|
||||||
|
gridProps?: GridProps;
|
||||||
|
formItemOptions?: FormItemOptions<ModalValue>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表单实例
|
||||||
|
*/
|
||||||
|
export type FormProInst<ModalValue extends FormModelValue = {}> = {} & FormInst;
|
|
@ -0,0 +1,3 @@
|
||||||
|
export type { IconProps } from "./src/interface";
|
||||||
|
export { default as Icon } from "./src/Icon.vue";
|
||||||
|
export { default as SelectIcon } from "./src/SelectIcon.vue";
|
|
@ -0,0 +1,56 @@
|
||||||
|
<template>
|
||||||
|
<div class="icon-container">
|
||||||
|
<component
|
||||||
|
:is="iconComponent?.component"
|
||||||
|
:style="styles"
|
||||||
|
:class="props.class"
|
||||||
|
/>
|
||||||
|
<span>
|
||||||
|
<slot></slot>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, type CSSProperties } from "vue";
|
||||||
|
import type { IconProps } from "./interface";
|
||||||
|
import { iconMap } from "./icon";
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<IconProps>(), {
|
||||||
|
size: 20,
|
||||||
|
color: "currentColor",
|
||||||
|
});
|
||||||
|
|
||||||
|
const iconComponent = computed(() => iconMap.get(props.name));
|
||||||
|
|
||||||
|
const styles = computed(() => {
|
||||||
|
const style: CSSProperties = {};
|
||||||
|
if (typeof props.size === "number") {
|
||||||
|
style.width = `${props.size}px`;
|
||||||
|
style.height = `${props.size}px`;
|
||||||
|
} else {
|
||||||
|
style.width = props.size;
|
||||||
|
style.height = props.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.color) {
|
||||||
|
style.color = props.color;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof props.style === "string") {
|
||||||
|
return props.style;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...style,
|
||||||
|
...props.style,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.icon-container {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,7 @@
|
||||||
|
<template><n-select v-bind="props">
|
||||||
|
</n-select></template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { selectIconProps } from './selectIcon';
|
||||||
|
const props = defineProps(selectIconProps)
|
||||||
|
</script>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<template>
|
||||||
|
<Icon name="LineMdMenuFoldRight" />
|
||||||
|
<Icon name="LineMdMenuFoldRight" />
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
</script>
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { type Component } from "vue";
|
||||||
|
import { markRaw } from "vue";
|
||||||
|
|
||||||
|
export const modules = import.meta.glob("./icons/*.vue", { eager: true });
|
||||||
|
|
||||||
|
// 创建图标映射
|
||||||
|
export const iconMap = new Map<
|
||||||
|
string,
|
||||||
|
{
|
||||||
|
title: string;
|
||||||
|
component: Component;
|
||||||
|
}
|
||||||
|
>();
|
||||||
|
|
||||||
|
Object.values(modules).forEach((module: any) => {
|
||||||
|
const component = module.default;
|
||||||
|
if (component?.name) {
|
||||||
|
iconMap.set(component.name, {
|
||||||
|
title: component.title,
|
||||||
|
component: markRaw(component),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,24 @@
|
||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 32 32"
|
||||||
|
>
|
||||||
|
<!-- Icon from Carbon by IBM - undefined -->
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M16 4c6.6 0 12 5.4 12 12s-5.4 12-12 12S4 22.6 4 16S9.4 4 16 4m0-2C8.3 2 2 8.3 2 16s6.3 14 14 14s14-6.3 14-14S23.7 2 16 2"
|
||||||
|
></path>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M8 15h16v2H8z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "CarbonSubtractAlt",
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,28 @@
|
||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
>
|
||||||
|
<!-- Icon from Codicons by Microsoft Corporation - https://github.com/microsoft/vscode-codicons/blob/main/LICENSE -->
|
||||||
|
<g
|
||||||
|
fill="currentColor"
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="m8.621 8.086l-.707-.707L6.5 8.793L5.086 7.379l-.707.707L5.793 9.5l-1.414 1.414l.707.707L6.5 10.207l1.414 1.414l.707-.707L7.207 9.5z"
|
||||||
|
></path>
|
||||||
|
<path
|
||||||
|
d="m5 3l1-1h7l1 1v7l-1 1h-2v2l-1 1H3l-1-1V6l1-1h2zm1 2h4l1 1v4h2V3H6zm4 1H3v7h7z"
|
||||||
|
></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "CodiconCloseAll",
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,23 @@
|
||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
>
|
||||||
|
<!-- Icon from Codicons by Microsoft Corporation - https://github.com/microsoft/vscode-codicons/blob/main/LICENSE -->
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
fillRule="evenodd"
|
||||||
|
d="M9.111 4.663A2 2 0 1 1 6.89 1.337a2 2 0 0 1 2.222 3.326zm-.555-2.494A1 1 0 1 0 7.444 3.83a1 1 0 0 0 1.112-1.66zm2.61.03a1.494 1.494 0 0 1 1.895.188a1.513 1.513 0 0 1-.487 2.46a1.49 1.49 0 0 1-1.635-.326a1.512 1.512 0 0 1 .228-2.321zm.48 1.61a.499.499 0 1 0 .705-.708a.5.5 0 0 0-.351-.15a.5.5 0 0 0-.5.503a.5.5 0 0 0 .146.356zM3.19 12.487H5v1.005H3.19a1.2 1.2 0 0 1-.842-.357a1.2 1.2 0 0 1-.348-.85v-1.81a1 1 0 0 1-.71-.332A1 1 0 0 1 1 9.408V7.226c.003-.472.19-.923.52-1.258c.329-.331.774-.52 1.24-.523H4.6a2.9 2.9 0 0 0-.55 1.006H2.76a.8.8 0 0 0-.54.232a.78.78 0 0 0-.22.543v2.232h1v2.826a.2.2 0 0 0 .05.151a.24.24 0 0 0 .14.05zm7.3-6.518a1.77 1.77 0 0 0-1.25-.523H6.76a1.77 1.77 0 0 0-1.24.523c-.33.335-.517.786-.52 1.258v3.178a1.06 1.06 0 0 0 .29.734a1 1 0 0 0 .71.332v2.323a1.2 1.2 0 0 0 .35.855c.18.168.407.277.65.312h2a1.15 1.15 0 0 0 1-1.167V11.47a1 1 0 0 0 .71-.332a1 1 0 0 0 .29-.734V7.226a1.8 1.8 0 0 0-.51-1.258zM10 10.454H9v3.34a.2.2 0 0 1-.06.14a.17.17 0 0 1-.14.06H7.19a.21.21 0 0 1-.2-.2v-3.34H6V7.226c0-.203.079-.398.22-.543a.8.8 0 0 1 .54-.232h2.48a.78.78 0 0 1 .705.48a.8.8 0 0 1 .055.295zm2.81 3.037H11v-1.005h1.8a.24.24 0 0 0 .14-.05a.2.2 0 0 0 .06-.152V9.458h1V7.226a.78.78 0 0 0-.22-.543a.8.8 0 0 0-.54-.232h-1.29a2.9 2.9 0 0 0-.55-1.006h1.84a1.77 1.77 0 0 1 1.24.523c.33.335.517.786.52 1.258v2.182c0 .273-.103.535-.289.733c-.186.199-.44.318-.711.333v1.81c0 .319-.125.624-.348.85a1.2 1.2 0 0 1-.842.357M4 1.945a1.49 1.49 0 0 0-1.386.932A1.52 1.52 0 0 0 2.94 4.52A1.497 1.497 0 0 0 5.5 3.454c0-.4-.158-.784-.44-1.067A1.5 1.5 0 0 0 4 1.945m0 2.012a.5.5 0 0 1-.5-.503a.504.504 0 0 1 .5-.503a.51.51 0 0 1 .5.503a.504.504 0 0 1-.5.503"
|
||||||
|
clipRule="evenodd"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "CodiconOrganization",
|
||||||
|
title: "部门管理",
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,25 @@
|
||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<!-- Icon from EOS Icons by SUSE UX/UI team - https://gitlab.com/SUSE-UIUX/eos-icons/-/blob/master/LICENSE -->
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M15 21h-2a2 2 0 0 1 0-4h2v-2h-2a4 4 0 0 0 0 8h2Zm8-2a4 4 0 0 1-4 4h-2v-2h2a2 2 0 0 0 0-4h-2v-2h2a4 4 0 0 1 4 4"
|
||||||
|
></path>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M14 18h4v2h-4zm-7 1a6 6 0 0 1 .09-1H3v-1.4c0-2 4-3.1 6-3.1a8.6 8.6 0 0 1 1.35.125A5.95 5.95 0 0 1 13 13h5V4a2.006 2.006 0 0 0-2-2h-4.18a2.988 2.988 0 0 0-5.64 0H2a2.006 2.006 0 0 0-2 2v14a2.006 2.006 0 0 0 2 2h5.09A6 6 0 0 1 7 19M9 2a1 1 0 1 1-1 1a1.003 1.003 0 0 1 1-1m0 4a3 3 0 1 1-3 3a2.996 2.996 0 0 1 3-3"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "EosIconsRoleBinding",
|
||||||
|
title: "角色管理",
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,29 @@
|
||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<!-- Icon from EOS Icons by SUSE UX/UI team - https://gitlab.com/SUSE-UIUX/eos-icons/-/blob/master/LICENSE -->
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M23 19a4 4 0 0 1-4 4h-2v-2h2a2 2 0 0 0 0-4h-2v-2h2a4 4 0 0 1 4 4M9 19a4 4 0 0 1 4-4h2v2h-2a2 2 0 0 0 0 4h2v2h-2a4 4 0 0 1-4-4"
|
||||||
|
></path>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M14 18h4v2h-4zM9 5a3 3 0 1 0 3 3a3.01 3.01 0 0 0-3-3m0 4a1 1 0 1 1 1-1a1.003 1.003 0 0 1-1 1m-3.69 6A7 7 0 0 1 9 13.88a6 6 0 0 1 .778.064A5.97 5.97 0 0 1 13 13h.254A9.4 9.4 0 0 0 9 11.89c-2.03 0-6 1.07-6 3.58V17h4.349a6 6 0 0 1 1.188-2Z"
|
||||||
|
></path>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M16 2h-4.18a2.988 2.988 0 0 0-5.64 0H2a2.006 2.006 0 0 0-2 2v14a2.006 2.006 0 0 0 2 2h5.141a3.6 3.6 0 0 1 0-2H2V4h14v9h2V4a2.006 2.006 0 0 0-2-2M9 3.25a.756.756 0 0 1-.75-.75a.75.75 0 0 1 1.5 0a.756.756 0 0 1-.75.75"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "EosIconsRoleBindingOutlined",
|
||||||
|
title: "角色管理",
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,20 @@
|
||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<!-- Icon from Google Material Icons by Material Design Authors - https://github.com/material-icons/material-icons/blob/master/LICENSE -->
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6l-6-6z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "IcBaselineKeyboardArrowDown",
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,27 @@
|
||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<!-- Icon from IconaMoon by Dariush Habibpour - https://creativecommons.org/licenses/by/4.0/ -->
|
||||||
|
<g
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
>
|
||||||
|
<path d="M3 12a9 9 0 0 1 16-5.658"></path>
|
||||||
|
<path d="M19.5 3v4h-4m5.5 5a9 9 0 0 1-16 5.657"></path>
|
||||||
|
<path d="M4.5 21v-4h4"></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "IconamoonSynchronize",
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,46 @@
|
||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<!-- Icon from Material Line Icons by Vjacheslav Trushkin - https://github.com/cyberalien/line-md/blob/master/license.txt -->
|
||||||
|
<g
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-dasharray="28"
|
||||||
|
stroke-dashoffset="28"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
>
|
||||||
|
<path d="M4 21v-1c0 -3.31 2.69 -6 6 -6h4c3.31 0 6 2.69 6 6v1">
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
dur="0.4s"
|
||||||
|
values="28;0"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
d="M12 11c-2.21 0 -4 -1.79 -4 -4c0 -2.21 1.79 -4 4 -4c2.21 0 4 1.79 4 4c0 2.21 -1.79 4 -4 4Z"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
begin="0.4s"
|
||||||
|
dur="0.4s"
|
||||||
|
values="28;0"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "LineMdAccount",
|
||||||
|
title: "用户",
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,110 @@
|
||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<!-- Icon from Material Line Icons by Vjacheslav Trushkin - https://github.com/cyberalien/line-md/blob/master/license.txt -->
|
||||||
|
<mask id="lineMdCogFilledLoop0">
|
||||||
|
<defs>
|
||||||
|
<symbol id="lineMdCogFilledLoop1">
|
||||||
|
<path
|
||||||
|
d="M11 13L15.74 5.5C16.03 5.67 16.31 5.85 16.57 6.05C16.57 6.05 16.57 6.05 16.57 6.05C16.64 6.1 16.71 6.16 16.77 6.22C18.14 7.34 19.09 8.94 19.4 10.75C19.41 10.84 19.42 10.92 19.43 11C19.43 11 19.43 11 19.43 11C19.48 11.33 19.5 11.66 19.5 12z"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="d"
|
||||||
|
begin="0.5s"
|
||||||
|
dur="0.2s"
|
||||||
|
values="M11 13L15.74 5.5C16.03 5.67 16.31 5.85 16.57 6.05C16.57 6.05 16.57 6.05 16.57 6.05C16.64 6.1 16.71 6.16 16.77 6.22C18.14 7.34 19.09 8.94 19.4 10.75C19.41 10.84 19.42 10.92 19.43 11C19.43 11 19.43 11 19.43 11C19.48 11.33 19.5 11.66 19.5 12z;M11 13L15.74 5.5C16.03 5.67 16.31 5.85 16.57 6.05C16.57 6.05 19.09 5.04 19.09 5.04C19.25 4.98 19.52 5.01 19.6 5.17C19.6 5.17 21.67 8.75 21.67 8.75C21.77 8.92 21.73 9.2 21.6 9.32C21.6 9.32 19.43 11 19.43 11C19.48 11.33 19.5 11.66 19.5 12z"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
</symbol>
|
||||||
|
</defs>
|
||||||
|
<g
|
||||||
|
fill="none"
|
||||||
|
stroke="#fff"
|
||||||
|
stroke-width="2"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-dasharray="36"
|
||||||
|
stroke-dashoffset="36"
|
||||||
|
stroke-width="5"
|
||||||
|
d="M12 7c2.76 0 5 2.24 5 5c0 2.76 -2.24 5 -5 5c-2.76 0 -5 -2.24 -5 -5c0 -2.76 2.24 -5 5 -5Z"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
dur="0.5s"
|
||||||
|
values="36;0"
|
||||||
|
></animate>
|
||||||
|
<set
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="opacity"
|
||||||
|
begin="0.5s"
|
||||||
|
to="0"
|
||||||
|
></set>
|
||||||
|
</path>
|
||||||
|
<g
|
||||||
|
fill="#fff"
|
||||||
|
stroke="none"
|
||||||
|
opacity="0"
|
||||||
|
>
|
||||||
|
<use href="#lineMdCogFilledLoop1"></use>
|
||||||
|
<use
|
||||||
|
href="#lineMdCogFilledLoop1"
|
||||||
|
transform="rotate(60 12 12)"
|
||||||
|
></use>
|
||||||
|
<use
|
||||||
|
href="#lineMdCogFilledLoop1"
|
||||||
|
transform="rotate(120 12 12)"
|
||||||
|
></use>
|
||||||
|
<use
|
||||||
|
href="#lineMdCogFilledLoop1"
|
||||||
|
transform="rotate(180 12 12)"
|
||||||
|
></use>
|
||||||
|
<use
|
||||||
|
href="#lineMdCogFilledLoop1"
|
||||||
|
transform="rotate(240 12 12)"
|
||||||
|
></use>
|
||||||
|
<use
|
||||||
|
href="#lineMdCogFilledLoop1"
|
||||||
|
transform="rotate(300 12 12)"
|
||||||
|
></use>
|
||||||
|
<set
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="opacity"
|
||||||
|
begin="0.5s"
|
||||||
|
to="1"
|
||||||
|
></set>
|
||||||
|
<animateTransform
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="transform"
|
||||||
|
dur="30s"
|
||||||
|
type="rotate"
|
||||||
|
values="0 12 12;360 12 12"
|
||||||
|
></animateTransform>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<circle
|
||||||
|
cx="12"
|
||||||
|
cy="12"
|
||||||
|
r="3.5"
|
||||||
|
></circle>
|
||||||
|
</mask>
|
||||||
|
<rect
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
fill="currentColor"
|
||||||
|
mask="url(#lineMdCogFilledLoop0)"
|
||||||
|
></rect>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "LineMdCogFilledLoop",
|
||||||
|
title: "设置",
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,77 @@
|
||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<!-- Icon from Material Line Icons by Vjacheslav Trushkin - https://github.com/cyberalien/line-md/blob/master/license.txt -->
|
||||||
|
<g
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-dasharray="64"
|
||||||
|
stroke-dashoffset="64"
|
||||||
|
d="M13 3l6 6v12h-14v-18h8"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
dur="0.6s"
|
||||||
|
values="64;0"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
stroke-dasharray="14"
|
||||||
|
stroke-dashoffset="14"
|
||||||
|
stroke-width="1"
|
||||||
|
d="M12.5 3v5.5h6.5"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
begin="0.7s"
|
||||||
|
dur="0.2s"
|
||||||
|
values="14;0"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
stroke-dasharray="8"
|
||||||
|
stroke-dashoffset="8"
|
||||||
|
d="M10 13l-2 2l2 2"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
begin="0.9s"
|
||||||
|
dur="0.2s"
|
||||||
|
values="8;0"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
stroke-dasharray="8"
|
||||||
|
stroke-dashoffset="8"
|
||||||
|
d="M14 13l2 2l-2 2"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
begin="1.1s"
|
||||||
|
dur="0.2s"
|
||||||
|
values="8;0"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "LineMdDocumentCode",
|
||||||
|
title: "开发设置",
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,91 @@
|
||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<!-- Icon from Material Line Icons by Vjacheslav Trushkin - https://github.com/cyberalien/line-md/blob/master/license.txt -->
|
||||||
|
<mask id="lineMdFileUploadFilled0">
|
||||||
|
<g
|
||||||
|
fill="none"
|
||||||
|
stroke="#fff"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="#fff"
|
||||||
|
fillOpacity="0"
|
||||||
|
stroke-dasharray="64"
|
||||||
|
stroke-dashoffset="64"
|
||||||
|
d="M13.5 3l5.5 5.5v11.5c0 0.55 -0.45 1 -1 1h-12c-0.55 0 -1 -0.45 -1 -1v-16c0 -0.55 0.45 -1 1 -1Z"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="fill-opacity"
|
||||||
|
begin="0.6s"
|
||||||
|
dur="0.5s"
|
||||||
|
values="0;1"
|
||||||
|
></animate>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
dur="0.6s"
|
||||||
|
values="64;0"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
fill="#000"
|
||||||
|
stroke="#000"
|
||||||
|
d="M14.5 3.5l0 4.5l4.5 0z"
|
||||||
|
opacity="0"
|
||||||
|
>
|
||||||
|
<set
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="opacity"
|
||||||
|
begin="0.6s"
|
||||||
|
to="1"
|
||||||
|
></set>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
d="M13.5 3l5.5 5.5"
|
||||||
|
opacity="0"
|
||||||
|
>
|
||||||
|
<set
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="opacity"
|
||||||
|
begin="0.6s"
|
||||||
|
to="1"
|
||||||
|
></set>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
fill="#000"
|
||||||
|
stroke="none"
|
||||||
|
d="M12 18l4 0h-2.5v0h-3v0h-2.5z"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="d"
|
||||||
|
begin="1.1s"
|
||||||
|
dur="0.2s"
|
||||||
|
values="M12 18l4 0h-2.5v0h-3v0h-2.5z;M12 11l4 4h-2.5v3h-3v-3h-2.5Z"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
</g>
|
||||||
|
</mask>
|
||||||
|
<rect
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
fill="currentColor"
|
||||||
|
mask="url(#lineMdFileUploadFilled0)"
|
||||||
|
></rect>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "LineMdFileUploadFilled",
|
||||||
|
title: "文件上传",
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,24 @@
|
||||||
|
<template><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em"
|
||||||
|
viewBox="0 0 24 24"><!-- Icon from Material Line Icons by Vjacheslav Trushkin - https://github.com/cyberalien/line-md/blob/master/license.txt -->
|
||||||
|
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
|
||||||
|
<path stroke-dasharray="16" stroke-dashoffset="16" d="M5 21h14">
|
||||||
|
<animate fill="freeze" attributeName="stroke-dashoffset" dur="0.2s" values="16;0"></animate>
|
||||||
|
</path>
|
||||||
|
<path stroke-dasharray="14" stroke-dashoffset="14" d="M5 21v-13M19 21v-13">
|
||||||
|
<animate fill="freeze" attributeName="stroke-dashoffset" begin="0.2s" dur="0.2s" values="14;0"></animate>
|
||||||
|
</path>
|
||||||
|
<path stroke-dasharray="24" stroke-dashoffset="24" d="M9 21v-8h6v8">
|
||||||
|
<animate fill="freeze" attributeName="stroke-dashoffset" begin="0.4s" dur="0.4s" values="24;0"></animate>
|
||||||
|
</path>
|
||||||
|
<path stroke-dasharray="28" stroke-dashoffset="28" d="M2 10l10 -8l10 8">
|
||||||
|
<animate fill="freeze" attributeName="stroke-dashoffset" begin="0.5s" dur="0.6s" values="28;0"></animate>
|
||||||
|
</path>
|
||||||
|
</g>
|
||||||
|
</svg></template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: 'LineMdHomeMd',
|
||||||
|
title:'首页'
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,63 @@
|
||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<!-- Icon from Material Line Icons by Vjacheslav Trushkin - https://github.com/cyberalien/line-md/blob/master/license.txt -->
|
||||||
|
<g
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-dasharray="16"
|
||||||
|
stroke-dashoffset="16"
|
||||||
|
d="M5 21h14"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
dur="0.2s"
|
||||||
|
values="16;0"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
stroke-dasharray="14"
|
||||||
|
stroke-dashoffset="14"
|
||||||
|
d="M5 21v-13M19 21v-13"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
begin="0.2s"
|
||||||
|
dur="0.2s"
|
||||||
|
values="14;0"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
stroke-dasharray="28"
|
||||||
|
stroke-dashoffset="28"
|
||||||
|
d="M2 10l10 -8l10 8"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
begin="0.4s"
|
||||||
|
dur="0.6s"
|
||||||
|
values="28;0"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "LineMdHomeSimple",
|
||||||
|
title: "主页",
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,128 @@
|
||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<!-- Icon from Material Line Icons by Vjacheslav Trushkin - https://github.com/cyberalien/line-md/blob/master/license.txt -->
|
||||||
|
<g
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-dasharray="2"
|
||||||
|
stroke-dashoffset="2"
|
||||||
|
d="M4 5h0.01"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
dur="0.1s"
|
||||||
|
values="2;0"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
stroke-dasharray="14"
|
||||||
|
stroke-dashoffset="14"
|
||||||
|
d="M8 5h12"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
begin="0.1s"
|
||||||
|
dur="0.2s"
|
||||||
|
values="14;0"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
stroke-dasharray="2"
|
||||||
|
stroke-dashoffset="2"
|
||||||
|
d="M4 10h0.01"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
begin="0.3s"
|
||||||
|
dur="0.1s"
|
||||||
|
values="2;0"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
stroke-dasharray="14"
|
||||||
|
stroke-dashoffset="14"
|
||||||
|
d="M8 10h12"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
begin="0.4s"
|
||||||
|
dur="0.2s"
|
||||||
|
values="14;0"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
stroke-dasharray="2"
|
||||||
|
stroke-dashoffset="2"
|
||||||
|
d="M4 15h0.01"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
begin="0.6s"
|
||||||
|
dur="0.1s"
|
||||||
|
values="2;0"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
stroke-dasharray="14"
|
||||||
|
stroke-dashoffset="14"
|
||||||
|
d="M8 15h12"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
begin="0.7s"
|
||||||
|
dur="0.2s"
|
||||||
|
values="14;0"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
stroke-dasharray="2"
|
||||||
|
stroke-dashoffset="2"
|
||||||
|
d="M4 20h0.01"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
begin="0.9s"
|
||||||
|
dur="0.1s"
|
||||||
|
values="2;0"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
stroke-dasharray="14"
|
||||||
|
stroke-dashoffset="14"
|
||||||
|
d="M8 20h12"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
begin="1s"
|
||||||
|
dur="0.2s"
|
||||||
|
values="14;0"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "LineMdList",
|
||||||
|
title: "扁平化菜单",
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,128 @@
|
||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<!-- Icon from Material Line Icons by Vjacheslav Trushkin - https://github.com/cyberalien/line-md/blob/master/license.txt -->
|
||||||
|
<g
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-dasharray="2"
|
||||||
|
stroke-dashoffset="2"
|
||||||
|
d="M4 5h0.01"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
dur="0.1s"
|
||||||
|
values="2;0"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
stroke-dasharray="14"
|
||||||
|
stroke-dashoffset="14"
|
||||||
|
d="M8 5h12"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
begin="0.1s"
|
||||||
|
dur="0.2s"
|
||||||
|
values="14;0"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
stroke-dasharray="2"
|
||||||
|
stroke-dashoffset="2"
|
||||||
|
d="M6 10h0.01"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
begin="0.3s"
|
||||||
|
dur="0.1s"
|
||||||
|
values="2;0"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
stroke-dasharray="14"
|
||||||
|
stroke-dashoffset="14"
|
||||||
|
d="M10 10h10"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
begin="0.4s"
|
||||||
|
dur="0.2s"
|
||||||
|
values="14;0"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
stroke-dasharray="2"
|
||||||
|
stroke-dashoffset="2"
|
||||||
|
d="M8 15h0.01"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
begin="0.6s"
|
||||||
|
dur="0.1s"
|
||||||
|
values="2;0"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
stroke-dasharray="14"
|
||||||
|
stroke-dashoffset="14"
|
||||||
|
d="M12 15h8"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
begin="0.7s"
|
||||||
|
dur="0.2s"
|
||||||
|
values="14;0"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
stroke-dasharray="2"
|
||||||
|
stroke-dashoffset="2"
|
||||||
|
d="M10 20h0.01"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
begin="0.9s"
|
||||||
|
dur="0.1s"
|
||||||
|
values="2;0"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
stroke-dasharray="14"
|
||||||
|
stroke-dashoffset="14"
|
||||||
|
d="M14 20h6"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
begin="1s"
|
||||||
|
dur="0.2s"
|
||||||
|
values="14;0"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "LineMdListIndented",
|
||||||
|
title: "菜单管理",
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,62 @@
|
||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<!-- Icon from Material Line Icons by Vjacheslav Trushkin - https://github.com/cyberalien/line-md/blob/master/license.txt -->
|
||||||
|
<g
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-dasharray="48"
|
||||||
|
stroke-dashoffset="48"
|
||||||
|
d="M16 5v-1c0 -0.55 -0.45 -1 -1 -1h-9c-0.55 0 -1 0.45 -1 1v16c0 0.55 0.45 1 1 1h9c0.55 0 1 -0.45 1 -1v-1"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
dur="0.6s"
|
||||||
|
values="48;0"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
stroke-dasharray="12"
|
||||||
|
stroke-dashoffset="12"
|
||||||
|
d="M10 12h11"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
begin="0.7s"
|
||||||
|
dur="0.2s"
|
||||||
|
values="12;0"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
stroke-dasharray="6"
|
||||||
|
stroke-dashoffset="6"
|
||||||
|
d="M21 12l-3.5 -3.5M21 12l-3.5 3.5"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
begin="0.9s"
|
||||||
|
dur="0.2s"
|
||||||
|
values="6;0"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "LineMdLogOut",
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,24 @@
|
||||||
|
<template><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em"
|
||||||
|
viewBox="0 0 24 24"><!-- Icon from Material Line Icons by Vjacheslav Trushkin - https://github.com/cyberalien/line-md/blob/master/license.txt -->
|
||||||
|
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
|
||||||
|
<path stroke-dasharray="16" stroke-dashoffset="16" d="M19 5h-14">
|
||||||
|
<animate fill="freeze" attributeName="stroke-dashoffset" dur="0.2s" values="16;0"></animate>
|
||||||
|
</path>
|
||||||
|
<path stroke-dasharray="10" stroke-dashoffset="10" d="M19 12h-9">
|
||||||
|
<animate fill="freeze" attributeName="stroke-dashoffset" begin="0.2s" dur="0.2s" values="10;0"></animate>
|
||||||
|
</path>
|
||||||
|
<path stroke-dasharray="16" stroke-dashoffset="16" d="M19 19h-14">
|
||||||
|
<animate fill="freeze" attributeName="stroke-dashoffset" begin="0.4s" dur="0.2s" values="16;0"></animate>
|
||||||
|
</path>
|
||||||
|
<path stroke-dasharray="10" stroke-dashoffset="10" d="M7 9l-3 3l3 3">
|
||||||
|
<animate fill="freeze" attributeName="stroke-dashoffset" begin="0.6s" dur="0.2s" values="10;0"></animate>
|
||||||
|
</path>
|
||||||
|
</g>
|
||||||
|
</svg></template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: 'LineMdMenuFoldLeft',
|
||||||
|
title:"菜单向左折叠"
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,24 @@
|
||||||
|
<template><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em"
|
||||||
|
viewBox="0 0 24 24"><!-- Icon from Material Line Icons by Vjacheslav Trushkin - https://github.com/cyberalien/line-md/blob/master/license.txt -->
|
||||||
|
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
|
||||||
|
<path stroke-dasharray="16" stroke-dashoffset="16" d="M5 5h14">
|
||||||
|
<animate fill="freeze" attributeName="stroke-dashoffset" dur="0.2s" values="16;0"></animate>
|
||||||
|
</path>
|
||||||
|
<path stroke-dasharray="10" stroke-dashoffset="10" d="M5 12h9">
|
||||||
|
<animate fill="freeze" attributeName="stroke-dashoffset" begin="0.2s" dur="0.2s" values="10;0"></animate>
|
||||||
|
</path>
|
||||||
|
<path stroke-dasharray="16" stroke-dashoffset="16" d="M5 19h14">
|
||||||
|
<animate fill="freeze" attributeName="stroke-dashoffset" begin="0.4s" dur="0.2s" values="16;0"></animate>
|
||||||
|
</path>
|
||||||
|
<path stroke-dasharray="10" stroke-dashoffset="10" d="M17 9l3 3l-3 3">
|
||||||
|
<animate fill="freeze" attributeName="stroke-dashoffset" begin="0.6s" dur="0.2s" values="10;0"></animate>
|
||||||
|
</path>
|
||||||
|
</g>
|
||||||
|
</svg></template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: 'LineMdMenuFoldRight',
|
||||||
|
title:"菜单向右折叠"
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,163 @@
|
||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<!-- Icon from Material Line Icons by Vjacheslav Trushkin - https://github.com/cyberalien/line-md/blob/master/license.txt -->
|
||||||
|
<g
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-dasharray="4"
|
||||||
|
stroke-dashoffset="4"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path d="M13 4h1.5M13 4h-1.5M13 4v1.5M13 4v-1.5">
|
||||||
|
<animate
|
||||||
|
id="lineMdMoonAltLoop0"
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
begin="0.7s;lineMdMoonAltLoop0.begin+6s"
|
||||||
|
dur="0.4s"
|
||||||
|
values="4;0"
|
||||||
|
></animate>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
begin="lineMdMoonAltLoop0.begin+2s;lineMdMoonAltLoop0.begin+4s"
|
||||||
|
dur="0.4s"
|
||||||
|
values="4;0"
|
||||||
|
></animate>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
begin="lineMdMoonAltLoop0.begin+1.2s;lineMdMoonAltLoop0.begin+3.2s;lineMdMoonAltLoop0.begin+5.2s"
|
||||||
|
dur="0.4s"
|
||||||
|
values="0;4"
|
||||||
|
></animate>
|
||||||
|
<set
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="d"
|
||||||
|
begin="lineMdMoonAltLoop0.begin+1.8s"
|
||||||
|
to="M12 5h1.5M12 5h-1.5M12 5v1.5M12 5v-1.5"
|
||||||
|
></set>
|
||||||
|
<set
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="d"
|
||||||
|
begin="lineMdMoonAltLoop0.begin+3.8s"
|
||||||
|
to="M12 4h1.5M12 4h-1.5M12 4v1.5M12 4v-1.5"
|
||||||
|
></set>
|
||||||
|
<set
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="d"
|
||||||
|
begin="lineMdMoonAltLoop0.begin+5.8s"
|
||||||
|
to="M13 4h1.5M13 4h-1.5M13 4v1.5M13 4v-1.5"
|
||||||
|
></set>
|
||||||
|
</path>
|
||||||
|
<path d="M19 11h1.5M19 11h-1.5M19 11v1.5M19 11v-1.5">
|
||||||
|
<animate
|
||||||
|
id="lineMdMoonAltLoop1"
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
begin="1.1s;lineMdMoonAltLoop1.begin+6s"
|
||||||
|
dur="0.4s"
|
||||||
|
values="4;0"
|
||||||
|
></animate>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
begin="lineMdMoonAltLoop1.begin+2s;lineMdMoonAltLoop1.begin+4s"
|
||||||
|
dur="0.4s"
|
||||||
|
values="4;0"
|
||||||
|
></animate>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
begin="lineMdMoonAltLoop1.begin+1.2s;lineMdMoonAltLoop1.begin+3.2s;lineMdMoonAltLoop1.begin+5.2s"
|
||||||
|
dur="0.4s"
|
||||||
|
values="0;4"
|
||||||
|
></animate>
|
||||||
|
<set
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="d"
|
||||||
|
begin="lineMdMoonAltLoop1.begin+1.8s"
|
||||||
|
to="M17 11h1.5M17 11h-1.5M17 11v1.5M17 11v-1.5"
|
||||||
|
></set>
|
||||||
|
<set
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="d"
|
||||||
|
begin="lineMdMoonAltLoop1.begin+3.8s"
|
||||||
|
to="M18 12h1.5M18 12h-1.5M18 12v1.5M18 12v-1.5"
|
||||||
|
></set>
|
||||||
|
<set
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="d"
|
||||||
|
begin="lineMdMoonAltLoop1.begin+5.8s"
|
||||||
|
to="M19 11h1.5M19 11h-1.5M19 11v1.5M19 11v-1.5"
|
||||||
|
></set>
|
||||||
|
</path>
|
||||||
|
<path d="M19 4h1.5M19 4h-1.5M19 4v1.5M19 4v-1.5">
|
||||||
|
<animate
|
||||||
|
id="lineMdMoonAltLoop2"
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
begin="2s;lineMdMoonAltLoop2.begin+6s"
|
||||||
|
dur="0.4s"
|
||||||
|
values="4;0"
|
||||||
|
></animate>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
begin="lineMdMoonAltLoop2.begin+2s"
|
||||||
|
dur="0.4s"
|
||||||
|
values="4;0"
|
||||||
|
></animate>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
begin="lineMdMoonAltLoop2.begin+1.2s;lineMdMoonAltLoop2.begin+3.2s"
|
||||||
|
dur="0.4s"
|
||||||
|
values="0;4"
|
||||||
|
></animate>
|
||||||
|
<set
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="d"
|
||||||
|
begin="lineMdMoonAltLoop2.begin+1.8s"
|
||||||
|
to="M20 5h1.5M20 5h-1.5M20 5v1.5M20 5v-1.5"
|
||||||
|
></set>
|
||||||
|
<set
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="d"
|
||||||
|
begin="lineMdMoonAltLoop2.begin+5.8s"
|
||||||
|
to="M19 4h1.5M19 4h-1.5M19 4v1.5M19 4v-1.5"
|
||||||
|
></set>
|
||||||
|
</path>
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-dasharray="56"
|
||||||
|
stroke-dashoffset="56"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M7 6 C7 12.08 11.92 17 18 17 C18.53 17 19.05 16.96 19.56 16.89 C17.95 19.36 15.17 21 12 21 C7.03 21 3 16.97 3 12 C3 8.83 4.64 6.05 7.11 4.44 C7.04 4.95 7 5.47 7 6 Z"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
dur="0.6s"
|
||||||
|
values="56;0"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "LineMdMoonAltLoop",
|
||||||
|
title: "夜间主题",
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,155 @@
|
||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<!-- Icon from Material Line Icons by Vjacheslav Trushkin - https://github.com/cyberalien/line-md/blob/master/license.txt -->
|
||||||
|
<g
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-dasharray="2"
|
||||||
|
stroke-dashoffset="2"
|
||||||
|
d="M12 19v1M19 12h1M12 5v-1M5 12h-1"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="d"
|
||||||
|
begin="0.6s"
|
||||||
|
dur="0.2s"
|
||||||
|
values="M12 19v1M19 12h1M12 5v-1M5 12h-1;M12 21v1M21 12h1M12 3v-1M3 12h-1"
|
||||||
|
></animate>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
begin="0.6s"
|
||||||
|
dur="0.2s"
|
||||||
|
values="2;0"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
stroke-dasharray="2"
|
||||||
|
stroke-dashoffset="2"
|
||||||
|
d="M17 17l0.5 0.5M17 7l0.5 -0.5M7 7l-0.5 -0.5M7 17l-0.5 0.5"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="d"
|
||||||
|
begin="0.8s"
|
||||||
|
dur="0.2s"
|
||||||
|
values="M17 17l0.5 0.5M17 7l0.5 -0.5M7 7l-0.5 -0.5M7 17l-0.5 0.5;M18.5 18.5l0.5 0.5M18.5 5.5l0.5 -0.5M5.5 5.5l-0.5 -0.5M5.5 18.5l-0.5 0.5"
|
||||||
|
></animate>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
begin="0.8s"
|
||||||
|
dur="0.2s"
|
||||||
|
values="2;0"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
<animateTransform
|
||||||
|
attributeName="transform"
|
||||||
|
dur="30s"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
type="rotate"
|
||||||
|
values="0 12 12;360 12 12"
|
||||||
|
></animateTransform>
|
||||||
|
</g>
|
||||||
|
<mask id="lineMdMoonAltToSunnyOutlineLoopTransition0">
|
||||||
|
<circle
|
||||||
|
cx="12"
|
||||||
|
cy="12"
|
||||||
|
r="12"
|
||||||
|
fill="#fff"
|
||||||
|
></circle>
|
||||||
|
<circle
|
||||||
|
cx="12"
|
||||||
|
cy="12"
|
||||||
|
r="8"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="r"
|
||||||
|
dur="0.4s"
|
||||||
|
values="8;4"
|
||||||
|
></animate>
|
||||||
|
</circle>
|
||||||
|
<circle
|
||||||
|
cx="18"
|
||||||
|
cy="6"
|
||||||
|
r="12"
|
||||||
|
fill="#fff"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="cx"
|
||||||
|
dur="0.4s"
|
||||||
|
values="18;22"
|
||||||
|
></animate>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="cy"
|
||||||
|
dur="0.4s"
|
||||||
|
values="6;2"
|
||||||
|
></animate>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="r"
|
||||||
|
dur="0.4s"
|
||||||
|
values="12;3"
|
||||||
|
></animate>
|
||||||
|
</circle>
|
||||||
|
<circle
|
||||||
|
cx="18"
|
||||||
|
cy="6"
|
||||||
|
r="10"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="cx"
|
||||||
|
dur="0.4s"
|
||||||
|
values="18;22"
|
||||||
|
></animate>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="cy"
|
||||||
|
dur="0.4s"
|
||||||
|
values="6;2"
|
||||||
|
></animate>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="r"
|
||||||
|
dur="0.4s"
|
||||||
|
values="10;1"
|
||||||
|
></animate>
|
||||||
|
</circle>
|
||||||
|
</mask>
|
||||||
|
<circle
|
||||||
|
cx="12"
|
||||||
|
cy="12"
|
||||||
|
r="10"
|
||||||
|
mask="url(#lineMdMoonAltToSunnyOutlineLoopTransition0)"
|
||||||
|
fill="currentColor"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="r"
|
||||||
|
dur="0.4s"
|
||||||
|
values="10;6"
|
||||||
|
></animate>
|
||||||
|
</circle>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "LineMdMoonAltToSunnyOutlineLoopTransition",
|
||||||
|
title: "日间主题",
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,50 @@
|
||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<!-- Icon from Material Line Icons by Vjacheslav Trushkin - https://github.com/cyberalien/line-md/blob/master/license.txt -->
|
||||||
|
<g
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-dasharray="20"
|
||||||
|
stroke-dashoffset="20"
|
||||||
|
d="M12 5c1.66 0 3 1.34 3 3c0 1.66 -1.34 3 -3 3c-1.66 0 -3 -1.34 -3 -3c0 -1.66 1.34 -3 3 -3Z"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
dur="0.4s"
|
||||||
|
values="20;0"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
stroke-dasharray="36"
|
||||||
|
stroke-dashoffset="36"
|
||||||
|
d="M12 14c4 0 7 2 7 3v2h-14v-2c0 -1 3 -3 7 -3Z"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
begin="0.5s"
|
||||||
|
dur="0.5s"
|
||||||
|
values="36;0"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "LineMdPerson",
|
||||||
|
title: "用户",
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,57 @@
|
||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<!-- Icon from Material Line Icons by Vjacheslav Trushkin - https://github.com/cyberalien/line-md/blob/master/license.txt -->
|
||||||
|
<g
|
||||||
|
fill="currentColor"
|
||||||
|
fillOpacity="0"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-dasharray="20"
|
||||||
|
stroke-dashoffset="20"
|
||||||
|
d="M12 5c1.66 0 3 1.34 3 3c0 1.66 -1.34 3 -3 3c-1.66 0 -3 -1.34 -3 -3c0 -1.66 1.34 -3 3 -3Z"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
dur="0.4s"
|
||||||
|
values="20;0"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
stroke-dasharray="36"
|
||||||
|
stroke-dashoffset="36"
|
||||||
|
d="M12 14c4 0 7 2 7 3v2h-14v-2c0 -1 3 -3 7 -3Z"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
begin="0.5s"
|
||||||
|
dur="0.5s"
|
||||||
|
values="36;0"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="fill-opacity"
|
||||||
|
begin="1.1s"
|
||||||
|
dur="0.5s"
|
||||||
|
values="0;1"
|
||||||
|
></animate>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "LineMdPersonFilled",
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,59 @@
|
||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<!-- Icon from Material Line Icons by Vjacheslav Trushkin - https://github.com/cyberalien/line-md/blob/master/license.txt -->
|
||||||
|
<g
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
fillOpacity="0"
|
||||||
|
stroke-dasharray="20"
|
||||||
|
stroke-dashoffset="20"
|
||||||
|
d="M12 15h2v-6h2.5l-4.5 -4.5M12 15h-2v-6h-2.5l4.5 -4.5"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="fill-opacity"
|
||||||
|
begin="0.7s"
|
||||||
|
dur="0.5s"
|
||||||
|
values="0;1"
|
||||||
|
></animate>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
dur="0.4s"
|
||||||
|
values="20;0"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
stroke-dasharray="14"
|
||||||
|
stroke-dashoffset="14"
|
||||||
|
d="M6 19h12"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
fill="freeze"
|
||||||
|
attributeName="stroke-dashoffset"
|
||||||
|
begin="0.5s"
|
||||||
|
dur="0.2s"
|
||||||
|
values="14;0"
|
||||||
|
></animate>
|
||||||
|
</path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "LineMdUpload",
|
||||||
|
title: "文件上传",
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,31 @@
|
||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<!-- Icon from Lucide by Lucide Contributors - https://github.com/lucide-icons/lucide/blob/main/LICENSE -->
|
||||||
|
<g
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
>
|
||||||
|
<circle
|
||||||
|
cx="12"
|
||||||
|
cy="12"
|
||||||
|
r="10"
|
||||||
|
></circle>
|
||||||
|
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3m.08 4h.01"></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "LucideCircleHelp",
|
||||||
|
title: "问题",
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,20 @@
|
||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<!-- Icon from Material Symbols by Google - https://github.com/google/material-design-icons/blob/master/LICENSE -->
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="m8.4 17l3.6-3.6l3.6 3.6l1.4-1.4l-3.6-3.6L17 8.4L15.6 7L12 10.6L8.4 7L7 8.4l3.6 3.6L7 15.6zm3.6 5q-2.075 0-3.9-.788t-3.175-2.137T2.788 15.9T2 12t.788-3.9t2.137-3.175T8.1 2.788T12 2t3.9.788t3.175 2.137T21.213 8.1T22 12t-.788 3.9t-2.137 3.175t-3.175 2.138T12 22m0-2q3.35 0 5.675-2.325T20 12t-2.325-5.675T12 4T6.325 6.325T4 12t2.325 5.675T12 20m0-8"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "MaterialSymbolsCancelOutline",
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,21 @@
|
||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<!-- Icon from Material Symbols by Google - https://github.com/google/material-design-icons/blob/master/LICENSE -->
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M3 21v-5h2v3h3v2zm13 0v-2h3v-3h2v5zM3 8V3h5v2H5v3zm16 0V5h-3V3h5v5z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "MaterialSymbolsFullscreen",
|
||||||
|
title: "进入全屏",
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,21 @@
|
||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<!-- Icon from Material Symbols by Google - https://github.com/google/material-design-icons/blob/master/LICENSE -->
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M6 21v-3H3v-2h5v5zm10 0v-5h5v2h-3v3zM3 8V6h3V3h2v5zm13 0V3h2v3h3v2z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "MaterialSymbolsFullscreenExit",
|
||||||
|
title: "退出全屏",
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,21 @@
|
||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<!-- Icon from Material Symbols by Google - https://github.com/google/material-design-icons/blob/master/LICENSE -->
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M4 20v-2h2.75l-.4-.35q-1.225-1.225-1.787-2.662T4 12.05q0-2.775 1.663-4.937T10 4.25v2.1Q8.2 7 7.1 8.563T6 12.05q0 1.125.425 2.188T7.75 16.2l.25.25V14h2v6zm10-.25v-2.1q1.8-.65 2.9-2.212T18 11.95q0-1.125-.425-2.187T16.25 7.8L16 7.55V10h-2V4h6v2h-2.75l.4.35q1.225 1.225 1.788 2.663T20 11.95q0 2.775-1.662 4.938T14 19.75"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "MaterialSymbolsSync",
|
||||||
|
title: "刷新",
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,26 @@
|
||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<!-- Icon from MingCute Icon by MingCute Design - https://github.com/Richard9394/MingCute/blob/main/LICENSE -->
|
||||||
|
<g fill="none">
|
||||||
|
<path
|
||||||
|
d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z"
|
||||||
|
></path>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M12 3a3 3 0 0 0-1 5.83V11H8a3 3 0 0 0-3 3v1.17a3.001 3.001 0 1 0 2 0V14a1 1 0 0 1 1-1h8a1 1 0 0 1 1 1v1.17a3.001 3.001 0 1 0 2 0V14a3 3 0 0 0-3-3h-3V8.83A3.001 3.001 0 0 0 12 3"
|
||||||
|
></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "MingcuteDepartmentFill",
|
||||||
|
title: "部门管理",
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,29 @@
|
||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<!-- Icon from MingCute Icon by MingCute Design - https://github.com/Richard9394/MingCute/blob/main/LICENSE -->
|
||||||
|
<g
|
||||||
|
fill="none"
|
||||||
|
fillRule="evenodd"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z"
|
||||||
|
></path>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M15 6a3 3 0 0 1-2 2.83V11h3a3 3 0 0 1 3 3v1.17a3.001 3.001 0 1 1-2 0V14a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v1.17a3.001 3.001 0 1 1-2 0V14a3 3 0 0 1 3-3h3V8.83A3.001 3.001 0 1 1 15 6m-3-1a1 1 0 1 0 0 2a1 1 0 0 0 0-2M6 17a1 1 0 1 0 0 2a1 1 0 0 0 0-2m12 0a1 1 0 1 0 0 2a1 1 0 0 0 0-2"
|
||||||
|
></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "MingcuteDepartmentLine",
|
||||||
|
title: "部门管理",
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,21 @@
|
||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 256 256"
|
||||||
|
>
|
||||||
|
<!-- Icon from Phosphor by Phosphor Icons - https://github.com/phosphor-icons/core/blob/main/LICENSE -->
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M208 20h-40a12 12 0 0 0 0 24h11l-15.64 15.67A68 68 0 1 0 108 178.92V192H88a12 12 0 0 0 0 24h20v16a12 12 0 0 0 24 0v-16h20a12 12 0 0 0 0-24h-20v-13.08a67.93 67.93 0 0 0 46.9-100.84L196 61v11a12 12 0 0 0 24 0V32a12 12 0 0 0-12-12m-88 136a44 44 0 1 1 44-44a44.05 44.05 0 0 1-44 44"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "PhGenderIntersexBold",
|
||||||
|
title: "性别",
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,19 @@
|
||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
class="inline-block"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M21 3v6h-2V6.41l-3.29 3.3l-1.42-1.42L17.59 5H15V3zM3 3v6h2V6.41l3.29 3.3l1.42-1.42L6.41 5H9V3zm18 18v-6h-2v2.59l-3.29-3.29l-1.41 1.41L17.59 19H15v2zM9 21v-2H6.41l3.29-3.29l-1.41-1.42L5 17.59V15H3v6z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "test",
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,9 @@
|
||||||
|
import type { CSSProperties } from "vue";
|
||||||
|
|
||||||
|
export interface IconProps {
|
||||||
|
name: string;
|
||||||
|
size?: number | string;
|
||||||
|
color?: string;
|
||||||
|
class?: string;
|
||||||
|
style?: CSSProperties | Record<string, string>;
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
import { NFlex, selectProps, type SelectRenderLabel } from "naive-ui";
|
||||||
|
import type { PropType } from "vue";
|
||||||
|
import { Icon } from "..";
|
||||||
|
import type { SelectBaseOption } from "naive-ui/es/select/src/interface";
|
||||||
|
import { iconMap } from "./icon";
|
||||||
|
|
||||||
|
export const selectIconProps = {
|
||||||
|
...selectProps,
|
||||||
|
clearable: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
renderLabel: {
|
||||||
|
type: Function as PropType<SelectRenderLabel>,
|
||||||
|
default: (option: SelectBaseOption) => {
|
||||||
|
return (
|
||||||
|
<NFlex class={"margin-xs"}>
|
||||||
|
<Icon name={option.value as string} />
|
||||||
|
--<label>{option.label}</label>
|
||||||
|
</NFlex>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
type: Array as PropType<SelectBaseOption[]>,
|
||||||
|
default: () =>
|
||||||
|
Array.from(iconMap.entries()).map(([k, v]) => ({
|
||||||
|
label: v.title || k,
|
||||||
|
value: k,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
};
|
|
@ -0,0 +1,5 @@
|
||||||
|
export * from "./layout";
|
||||||
|
export * from "./formPro";
|
||||||
|
export * from "./tablePro";
|
||||||
|
export * from "./icon";
|
||||||
|
export * from "./tabsPro";
|
|
@ -0,0 +1,50 @@
|
||||||
|
<template>
|
||||||
|
<!-- fill="#1e5efd" -->
|
||||||
|
<svg
|
||||||
|
class="inline-block text-35px text-primary"
|
||||||
|
width="30px"
|
||||||
|
height="30px"
|
||||||
|
version="1.0"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 640.000000 640.000000"
|
||||||
|
preserveAspectRatio="xMidYMid meet"
|
||||||
|
>
|
||||||
|
<g
|
||||||
|
transform="translate(0.000000,640.000000) scale(0.100000,-0.100000)"
|
||||||
|
fill="#18a058"
|
||||||
|
stroke="none"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M3045 5731 c-138 -23 -319 -101 -427 -185 -75 -59 -157 -148 -198
|
||||||
|
-215 -14 -23 -56 -90 -94 -149 -38 -59 -79 -123 -91 -142 -59 -94 -128 -200
|
||||||
|
-158 -242 -17 -25 -45 -70 -62 -98 -16 -28 -44 -70 -61 -93 -17 -23 -39 -58
|
||||||
|
-49 -77 -9 -19 -24 -43 -33 -53 -8 -9 -40 -57 -71 -106 -87 -137 -159 -249
|
||||||
|
-356 -551 -59 -91 -117 -181 -130 -200 -12 -19 -53 -82 -91 -140 -38 -58 -71
|
||||||
|
-109 -73 -115 -2 -5 -50 -79 -105 -165 -56 -85 -135 -207 -176 -270 -102 -159
|
||||||
|
-165 -257 -245 -380 -37 -58 -82 -127 -100 -155 -18 -27 -75 -115 -127 -195
|
||||||
|
-53 -80 -101 -156 -108 -170 -7 -14 -26 -41 -42 -60 -15 -19 -28 -39 -28 -44
|
||||||
|
0 -5 -11 -24 -25 -42 -14 -18 -25 -35 -25 -38 0 -3 -14 -26 -32 -52 -80 -119
|
||||||
|
-120 -305 -98 -460 25 -173 117 -335 260 -455 112 -94 205 -141 375 -191 72
|
||||||
|
-21 99 -23 280 -22 168 0 218 4 315 23 63 13 126 28 140 33 14 5 46 13 72 18
|
||||||
|
27 5 69 19 95 31 27 12 64 28 83 36 119 49 291 150 390 229 140 110 330 318
|
||||||
|
415 453 78 124 177 320 169 333 -3 4 -112 8 -242 8 -130 0 -232 4 -226 8 6 4
|
||||||
|
240 355 519 780 279 424 510 772 513 772 4 0 66 -89 139 -198 72 -108 307
|
||||||
|
-459 522 -779 215 -320 391 -584 391 -587 0 -3 -106 -6 -236 -6 l-235 0 7 -22
|
||||||
|
c28 -91 179 -343 277 -463 78 -96 226 -241 307 -303 73 -56 251 -172 263 -172
|
||||||
|
4 0 41 -16 84 -36 43 -20 96 -45 118 -55 22 -10 69 -25 105 -32 36 -8 119 -27
|
||||||
|
185 -42 96 -21 156 -27 302 -32 165 -5 190 -4 280 17 205 47 357 129 476 254
|
||||||
|
32 33 70 81 84 106 15 25 35 55 44 68 10 13 30 60 46 105 26 75 28 94 28 222
|
||||||
|
-1 127 -3 147 -28 213 -26 71 -73 156 -154 278 -21 31 -42 64 -48 73 -5 9 -37
|
||||||
|
59 -71 111 -34 52 -70 109 -80 126 -11 17 -44 69 -74 115 -30 47 -59 92 -65
|
||||||
|
102 -5 9 -26 40 -46 69 -20 29 -67 100 -104 158 -38 58 -81 124 -97 147 -15
|
||||||
|
23 -28 44 -28 48 0 4 -9 19 -20 33 -22 28 -109 162 -176 270 -21 35 -49 76
|
||||||
|
-60 90 -12 15 -30 43 -40 62 -11 19 -46 76 -78 125 -264 402 -266 405 -376
|
||||||
|
578 -44 70 -206 320 -267 413 -7 11 -20 33 -29 49 -10 17 -34 53 -56 82 -21
|
||||||
|
29 -38 55 -38 58 0 3 -43 71 -95 150 -52 79 -98 152 -102 162 -4 10 -15 27
|
||||||
|
-26 38 -10 11 -25 33 -33 48 -8 15 -43 70 -77 122 -34 52 -83 127 -109 167
|
||||||
|
-59 90 -138 180 -192 217 -23 15 -62 42 -87 59 -55 38 -166 85 -264 113 -78
|
||||||
|
23 -330 34 -420 20z"
|
||||||
|
></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</template>
|
|
@ -1,153 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="layout-container">
|
|
||||||
<div
|
|
||||||
class="layout_left"
|
|
||||||
:class="{ collapsed: useSetting.collapsed ? true : false }"
|
|
||||||
>
|
|
||||||
<Logo :collapsed="useSetting.collapsed"></Logo>
|
|
||||||
<!-- 展示菜单 -->
|
|
||||||
<div class="layout_left_item">
|
|
||||||
<a-menu
|
|
||||||
style="padding-right: 14px; width: 100%"
|
|
||||||
theme="light"
|
|
||||||
:inlineCollapsed="useSetting.collapsed ? true : false"
|
|
||||||
mode="inline"
|
|
||||||
v-model:openKeys="openKeys"
|
|
||||||
v-model:selectedKeys="selectedKeys"
|
|
||||||
>
|
|
||||||
<!-- :selectedKeys="selectedKeys" -->
|
|
||||||
<Menu-item :menu-item="userStore.menuRoutes"></Menu-item>
|
|
||||||
</a-menu>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="layout_tabbar"
|
|
||||||
:class="{ collapsed: useSetting.collapsed ? true : false }"
|
|
||||||
>
|
|
||||||
<tabbar v-model:collapsed="useSetting.collapsed"></tabbar>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="layout_content"
|
|
||||||
:class="{ collapsed: useSetting.collapsed ? true : false }"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
style="
|
|
||||||
height: 40px;
|
|
||||||
width: 100%;
|
|
||||||
background-color: #fff;
|
|
||||||
border-top: #ccc 1px solid;
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<tabsPane></tabsPane>
|
|
||||||
</div>
|
|
||||||
<div class="layout_content_item">
|
|
||||||
<router-view v-slot="{ Component, route }">
|
|
||||||
<transition appear name="fade" mode="out-in">
|
|
||||||
<keep-alive :include="keepAliveNames">
|
|
||||||
<component v-if="flag" :is="Component" :key="route.fullPath" />
|
|
||||||
</keep-alive>
|
|
||||||
</transition>
|
|
||||||
</router-view>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { ref, computed, watch, nextTick } from "vue";
|
|
||||||
import Logo from "./logo/index.vue";
|
|
||||||
import MenuItem from "./menuItem/MenuItem.vue";
|
|
||||||
import tabbar from "./tabbar/index.vue";
|
|
||||||
import tabsPane from "@/components/tabsPane/index.vue";
|
|
||||||
import { useUserStore } from "@/stores/modules/userStore";
|
|
||||||
import { useUserSetting } from "@/stores/modules/setting";
|
|
||||||
import { useRoute } from "vue-router";
|
|
||||||
import api from "@/axios";
|
|
||||||
const userStore = useUserStore();
|
|
||||||
const useSetting = useUserSetting();
|
|
||||||
const route = useRoute();
|
|
||||||
|
|
||||||
const keepAliveNames = ref<string[]>([]);
|
|
||||||
const openKeys = ref([useSetting.selectedKeys]);
|
|
||||||
|
|
||||||
const flag = ref(true);
|
|
||||||
// const resp = api.get("/management/auth/myAuthRouter");
|
|
||||||
// console.log(resp, "000");
|
|
||||||
|
|
||||||
// 全局刷新效果
|
|
||||||
watch(
|
|
||||||
() => useSetting.refresh,
|
|
||||||
() => {
|
|
||||||
flag.value = false;
|
|
||||||
nextTick(() => {
|
|
||||||
flag.value = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const selectedKeys = computed(() => [route.path]);
|
|
||||||
</script>
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.layout-container {
|
|
||||||
width: 100%;
|
|
||||||
height: 100vh;
|
|
||||||
// display: flex;
|
|
||||||
.layout_left {
|
|
||||||
width: 260px;
|
|
||||||
height: 100vh;
|
|
||||||
// background: #001529;
|
|
||||||
color: #fff;
|
|
||||||
transition: all 0.3s;
|
|
||||||
.layout_left_item {
|
|
||||||
height: calc(100vh - 50px);
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
&.collapsed {
|
|
||||||
width: 65px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.layout_tabbar {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 260px;
|
|
||||||
width: calc(100% - 260px);
|
|
||||||
height: 50px;
|
|
||||||
transition: all 0.3s;
|
|
||||||
background: #f5f5f5;
|
|
||||||
&.collapsed {
|
|
||||||
width: calc(100% - 65px);
|
|
||||||
left: 65px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.layout_content {
|
|
||||||
position: absolute;
|
|
||||||
width: calc(100% - 260px);
|
|
||||||
height: calc(100vh - 50px);
|
|
||||||
top: 50px;
|
|
||||||
left: 260px;
|
|
||||||
// padding: 20px;
|
|
||||||
overflow: auto;
|
|
||||||
transition: all 0.3s;
|
|
||||||
background: #f5f5f5;
|
|
||||||
&.collapsed {
|
|
||||||
width: calc(100% - 65px);
|
|
||||||
left: 65px;
|
|
||||||
}
|
|
||||||
.layout_content_item {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.fade-enter-from {
|
|
||||||
opacity: 0;
|
|
||||||
transition: all 0.2s;
|
|
||||||
transform: translateX(-30px);
|
|
||||||
}
|
|
||||||
// .fade-enter-active {
|
|
||||||
// opacity: 0;
|
|
||||||
// }
|
|
||||||
.fade-leave-to {
|
|
||||||
opacity: 0;
|
|
||||||
transition: all 0.2s;
|
|
||||||
transform: translateX(30px);
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,44 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="logo">
|
|
||||||
<img src="@/assets/vue.svg" alt="xxxx后台系统" />
|
|
||||||
<p
|
|
||||||
style="margin-left: 20px"
|
|
||||||
:class="{ logoItem: props.collapsed ? true : false }"
|
|
||||||
>
|
|
||||||
智慧食堂系统
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
const props = defineProps({
|
|
||||||
collapsed: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.logo {
|
|
||||||
height: 50px;
|
|
||||||
width: 100%;
|
|
||||||
color: black;
|
|
||||||
padding: 20px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
background: #fff;
|
|
||||||
border-bottom: 1px solid #ccc;
|
|
||||||
transition: all 0.3s;
|
|
||||||
img {
|
|
||||||
width: 30px;
|
|
||||||
height: 30px;
|
|
||||||
}
|
|
||||||
p {
|
|
||||||
font-size: 18px;
|
|
||||||
transition: all 1.2s;
|
|
||||||
}
|
|
||||||
.logoItem {
|
|
||||||
color: #fff;
|
|
||||||
transition: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,50 +0,0 @@
|
||||||
<template>
|
|
||||||
<template v-for="(item, index) in menuItem" :key="index">
|
|
||||||
<a-sub-menu v-if="item.children" :key="item.path">
|
|
||||||
<template #icon>
|
|
||||||
<SvgIcon :icon-name="item.icon"></SvgIcon>
|
|
||||||
</template>
|
|
||||||
<template #title>
|
|
||||||
<span style="margin-left: 5px">{{ item.name }}</span>
|
|
||||||
</template>
|
|
||||||
<menu-item :menu-item="item.children" />
|
|
||||||
</a-sub-menu>
|
|
||||||
<a-menu-item
|
|
||||||
v-else
|
|
||||||
:key="item.path + ''"
|
|
||||||
@click="routerMenuItem(item.path)"
|
|
||||||
>
|
|
||||||
<template #icon>
|
|
||||||
<SvgIcon :icon-name="item.icon"></SvgIcon>
|
|
||||||
</template>
|
|
||||||
<span style="margin-left: 5px">{{ item.name }}</span>
|
|
||||||
</a-menu-item>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { useRouter } from "vue-router";
|
|
||||||
import { SystemMenu } from "@/types/config/index";
|
|
||||||
import SvgIcon from "@/components/SvgIcon/index.vue";
|
|
||||||
import { useUserSetting } from "@/stores/modules/setting";
|
|
||||||
const router = useRouter();
|
|
||||||
const useSetting = useUserSetting();
|
|
||||||
withDefaults(
|
|
||||||
defineProps<{
|
|
||||||
menuItem?: SystemMenu[];
|
|
||||||
}>(),
|
|
||||||
{
|
|
||||||
menuItem: (): SystemMenu[] => {
|
|
||||||
return [];
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
const routerMenuItem = (path: string) => {
|
|
||||||
router.push(path);
|
|
||||||
useSetting.setSelectedKeys(path);
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.MenuItem {
|
|
||||||
padding-inline: 16px;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -0,0 +1,155 @@
|
||||||
|
<template>
|
||||||
|
<n-layout
|
||||||
|
has-sider
|
||||||
|
class="h_screen"
|
||||||
|
:class="{ dark: settingsStore.isDark }"
|
||||||
|
>
|
||||||
|
<n-layout-sider
|
||||||
|
style="box-shadow: rgba(100, 100, 111, 0.2) 0 7px 29px 0"
|
||||||
|
:bordered="true"
|
||||||
|
collapse-mode="width"
|
||||||
|
:collapsed-width="64"
|
||||||
|
:width="230"
|
||||||
|
:collapsed="collapsed"
|
||||||
|
@update:collapsed="collapsed = !collapsed"
|
||||||
|
>
|
||||||
|
<div class="title">
|
||||||
|
<transition enter-active-class="animate__animated animate__fadeInLeft">
|
||||||
|
<div
|
||||||
|
class="flex-center h-f"
|
||||||
|
:key="+collapsed"
|
||||||
|
>
|
||||||
|
<IndexIcon />
|
||||||
|
<span
|
||||||
|
style="margin-left: 10px"
|
||||||
|
v-if="!collapsed"
|
||||||
|
>
|
||||||
|
{{ platformName }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</div>
|
||||||
|
<LeftMenu />
|
||||||
|
</n-layout-sider>
|
||||||
|
<n-layout class="h-f">
|
||||||
|
<n-layout-header
|
||||||
|
class="l-header"
|
||||||
|
bordered
|
||||||
|
>
|
||||||
|
<TopHeader v-model:collapsed="collapsed" />
|
||||||
|
</n-layout-header>
|
||||||
|
<TopTabs class="l-tabs" />
|
||||||
|
<n-layout-content class="l-content">
|
||||||
|
<router-view v-slot="{ Component, route }">
|
||||||
|
<transition
|
||||||
|
name="fade-transform"
|
||||||
|
mode="out-in"
|
||||||
|
>
|
||||||
|
<keep-alive :include="cachedViews">
|
||||||
|
<component
|
||||||
|
:is="Component"
|
||||||
|
:key="route.fullPath"
|
||||||
|
v-if="isRouterShow"
|
||||||
|
/>
|
||||||
|
</keep-alive>
|
||||||
|
</transition>
|
||||||
|
</router-view>
|
||||||
|
</n-layout-content>
|
||||||
|
<n-layout-footer
|
||||||
|
bordered
|
||||||
|
position="absolute"
|
||||||
|
class="l-footer"
|
||||||
|
:style="{ background: settingsStore.isDark ? '#1e1e1e' : '#fff' }"
|
||||||
|
>
|
||||||
|
<div>Copyright © {{ dayjs().year() }} 湖南长沪信息科技有限公司</div>
|
||||||
|
</n-layout-footer>
|
||||||
|
</n-layout>
|
||||||
|
</n-layout>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import { useTabsStore } from "@/stores/tabsStore";
|
||||||
|
import { ref, computed, nextTick, provide } from "vue";
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
import LeftMenu from "./components/LeftMenu.vue";
|
||||||
|
import TopTabs from "./components/TopTabs.vue";
|
||||||
|
import TopHeader from "./components/TopHeader.vue";
|
||||||
|
import { useSettingsStore } from "@/stores/settings";
|
||||||
|
|
||||||
|
const settingsStore = useSettingsStore();
|
||||||
|
// import { useThemeVars } from "naive-ui";
|
||||||
|
// console.log(useThemeVars);
|
||||||
|
|
||||||
|
const tabStore = useTabsStore();
|
||||||
|
const route = useRoute();
|
||||||
|
const collapsed = ref(false);
|
||||||
|
const platformName = "保安可视化管理系统";
|
||||||
|
|
||||||
|
//缓存的组件列表
|
||||||
|
const cachedViews = computed(() =>
|
||||||
|
tabStore.tabList
|
||||||
|
.map((e) => (e.isKeepAlive ? e.name : null))
|
||||||
|
.filter((name): name is string => Boolean(name))
|
||||||
|
);
|
||||||
|
const isRouterShow = ref(true);
|
||||||
|
const refreshCurrentPage = () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
tabStore.changeTabKeepAlive(route.fullPath);
|
||||||
|
isRouterShow.value = false;
|
||||||
|
nextTick(() => {
|
||||||
|
tabStore.changeTabKeepAlive(route.fullPath);
|
||||||
|
isRouterShow.value = true;
|
||||||
|
});
|
||||||
|
}, 0);
|
||||||
|
};
|
||||||
|
//提供刷新页面方法 其实就是通过v-if显示销毁组件达到刷新页面的功能
|
||||||
|
provide("refreshCurrentPage", refreshCurrentPage);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.h_screen {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
width: 100%;
|
||||||
|
height: 50px;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.l-header {
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.l-tabs {
|
||||||
|
height: 35px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.l-content {
|
||||||
|
height: calc(100% - 85px);
|
||||||
|
padding: 4px 4px 10px;
|
||||||
|
min-height: 280px;
|
||||||
|
background-color: #f7fafc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.l-footer {
|
||||||
|
height: 50px;
|
||||||
|
margin-top: 10px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
/* background: white; */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
.l-content {
|
||||||
|
background-color: #1e1e1e; // 暗黑模式下的背景色,可自定义
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,52 @@
|
||||||
|
<template>
|
||||||
|
<n-menu
|
||||||
|
:value="route.fullPath"
|
||||||
|
:options="menuOptions"
|
||||||
|
></n-menu>
|
||||||
|
</template>
|
||||||
|
<script setup lang="tsx">
|
||||||
|
import { Icon } from "@/components";
|
||||||
|
import { INDEX_ROUTER, USER_CENTER } from "@/config/constant";
|
||||||
|
import { type AuthMenuTree, usePermissionStore } from "@/stores/permission";
|
||||||
|
import { isEmpty } from "lodash-es";
|
||||||
|
import type { MenuOption } from "naive-ui";
|
||||||
|
import { storeToRefs } from "pinia";
|
||||||
|
import { computed } from "vue";
|
||||||
|
import { RouterLink, useRoute } from "vue-router";
|
||||||
|
const route = useRoute();
|
||||||
|
const { authMenuList } = storeToRefs(usePermissionStore());
|
||||||
|
|
||||||
|
const convertMenuOptions = (data: AuthMenuTree[]): MenuOption[] => {
|
||||||
|
return data.map((item) => {
|
||||||
|
const opt: MenuOption = {
|
||||||
|
label: () =>
|
||||||
|
item.type.value === "menu" ? (
|
||||||
|
<RouterLink to={{ path: item.path }}>{item.name}</RouterLink>
|
||||||
|
) : (
|
||||||
|
item.name
|
||||||
|
),
|
||||||
|
key: item.path,
|
||||||
|
icon: () => <Icon name={item.icon}></Icon>,
|
||||||
|
};
|
||||||
|
if (!isEmpty(item.children)) {
|
||||||
|
opt.children = convertMenuOptions(item.children as AuthMenuTree[]);
|
||||||
|
}
|
||||||
|
return opt;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const menuOptions = computed<MenuOption[]>(() => {
|
||||||
|
const menus: MenuOption[] = convertMenuOptions(authMenuList.value);
|
||||||
|
menus.unshift({
|
||||||
|
label: () => (
|
||||||
|
<RouterLink to={{ path: INDEX_ROUTER.path }}>
|
||||||
|
{INDEX_ROUTER.meta.title}
|
||||||
|
</RouterLink>
|
||||||
|
),
|
||||||
|
key: INDEX_ROUTER.path,
|
||||||
|
icon: () => <Icon name={INDEX_ROUTER.meta.icon}></Icon>,
|
||||||
|
});
|
||||||
|
const menus_ = menus.filter((item) => item.key !== USER_CENTER.path);
|
||||||
|
return menus_;
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,79 @@
|
||||||
|
<template>
|
||||||
|
<div class="flex-justify-between h-f margin-left-lg margin-right-lg">
|
||||||
|
<n-space class="flex-center h-f">
|
||||||
|
<n-button
|
||||||
|
quaternary
|
||||||
|
@click="() => (collapsed = !collapsed)"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<Icon
|
||||||
|
:name="collapsed ? 'LineMdMenuFoldRight' : 'LineMdMenuFoldLeft'"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</n-button>
|
||||||
|
<n-breadcrumb>
|
||||||
|
<n-breadcrumb-item v-if="route.fullPath === INDEX_ROUTER.path">
|
||||||
|
<Icon
|
||||||
|
:name="INDEX_ROUTER?.meta?.icon"
|
||||||
|
size="18"
|
||||||
|
>
|
||||||
|
{{ INDEX_ROUTER?.meta?.title }}
|
||||||
|
</Icon>
|
||||||
|
</n-breadcrumb-item>
|
||||||
|
<n-breadcrumb-item
|
||||||
|
v-for="item in breadcrumbList"
|
||||||
|
:key="item.value"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
:name="item?.extData?.icon"
|
||||||
|
size="18"
|
||||||
|
>
|
||||||
|
{{ item.label }}
|
||||||
|
</Icon>
|
||||||
|
</n-breadcrumb-item>
|
||||||
|
</n-breadcrumb>
|
||||||
|
</n-space>
|
||||||
|
<n-space>
|
||||||
|
<FullScreen />
|
||||||
|
<ToggleTheme />
|
||||||
|
<UserAvatar />
|
||||||
|
</n-space>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="tsx">
|
||||||
|
import { INDEX_ROUTER } from "@/config/constant";
|
||||||
|
import { Icon } from "@/components/icon";
|
||||||
|
import { usePermissionStore } from "@/stores/permission";
|
||||||
|
import { computed } from "vue";
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
|
const permissionStore = usePermissionStore();
|
||||||
|
import FullScreen from "@/components/fullScreen/index.vue";
|
||||||
|
import ToggleTheme from "@/components/toggleTheme/index.vue";
|
||||||
|
import UserAvatar from "@/components/userAvatar/index.vue";
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
const collapsed = defineModel("collapsed");
|
||||||
|
const breadcrumbList = computed(() => {
|
||||||
|
const breadcrumbData =
|
||||||
|
permissionStore.breadcrumbList[
|
||||||
|
route.matched[route.matched.length - 1].path
|
||||||
|
] ?? [];
|
||||||
|
return !breadcrumbData || breadcrumbData.length === 0 ? [] : breadcrumbData;
|
||||||
|
});
|
||||||
|
|
||||||
|
//todo 先不做处理
|
||||||
|
const onBreadcrumbClick = (item: SelectNodeVo<string>, index: number) => {
|
||||||
|
if (index !== breadcrumbList.value.length - 1) {
|
||||||
|
router.push(item.value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
:deep(.n-breadcrumb-item__link) {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,135 @@
|
||||||
|
<template>
|
||||||
|
<TabsPro
|
||||||
|
:value="route.fullPath"
|
||||||
|
:tab-list="ts"
|
||||||
|
@update:value="onChange"
|
||||||
|
@handler-close="handleTabClose"
|
||||||
|
>
|
||||||
|
<template #actions>
|
||||||
|
<n-dropdown
|
||||||
|
trigger="click"
|
||||||
|
:options="suffixOptions"
|
||||||
|
show-arrow
|
||||||
|
@select="dropdownSelect"
|
||||||
|
>
|
||||||
|
<n-button
|
||||||
|
class="margin-right"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
更多
|
||||||
|
<IcBaselineKeyboardArrowDown />
|
||||||
|
</n-button>
|
||||||
|
</n-dropdown>
|
||||||
|
</template>
|
||||||
|
</TabsPro>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import IcBaselineKeyboardArrowDown from "@/components/icon/src/icons/IcBaselineKeyboardArrowDown.vue";
|
||||||
|
|
||||||
|
import CarbonSubtractAlt from "@/components/icon/src/icons/CarbonSubtractAlt.vue";
|
||||||
|
import CodiconCloseAll from "@/components/icon/src/icons/CodiconCloseAll.vue";
|
||||||
|
import IconamoonSynchronize from "@/components/icon/src/icons/IconamoonSynchronize.vue";
|
||||||
|
import MaterialSymbolsCancelOutline from "@/components/icon/src/icons/MaterialSymbolsCancelOutline.vue";
|
||||||
|
|
||||||
|
import { INDEX_ROUTER } from "@/config/constant";
|
||||||
|
import { type TabsMenuProps, useTabsStore } from "@/stores/tabsStore";
|
||||||
|
import { message } from "@/utils";
|
||||||
|
import { storeToRefs } from "pinia";
|
||||||
|
import { computed, inject, watch } from "vue";
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
|
import { TabsPro } from "@/components";
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
const tabStore = useTabsStore();
|
||||||
|
const { tabList } = storeToRefs(useTabsStore());
|
||||||
|
|
||||||
|
const ts = computed(() =>
|
||||||
|
tabList.value.map((e) => ({
|
||||||
|
value: e.path,
|
||||||
|
label: e.title,
|
||||||
|
icon: e.icon,
|
||||||
|
closable: e.closable,
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
const refreshCurrentPage = inject<Function>("refreshCurrentPage");
|
||||||
|
|
||||||
|
//监听路由的变化防止浏览器后退/前进不变化
|
||||||
|
watch(
|
||||||
|
() => route.fullPath,
|
||||||
|
() => {
|
||||||
|
//如果是全屏,不进行添加tab
|
||||||
|
if (route.meta.isFull) return;
|
||||||
|
const tabItem: TabsMenuProps = {
|
||||||
|
title: route.meta.title,
|
||||||
|
name: route.name as string,
|
||||||
|
path: route.fullPath,
|
||||||
|
icon: route.meta.icon as string,
|
||||||
|
isKeepAlive: route.meta.isKeepAlive,
|
||||||
|
closable: !route.meta.isFixed,
|
||||||
|
};
|
||||||
|
//添加tab
|
||||||
|
tabStore.addTab(tabItem);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const onChange = (fullPath: string) => {
|
||||||
|
router.push(fullPath);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTabClose = (val: string) => {
|
||||||
|
tabStore.closeTab(val);
|
||||||
|
};
|
||||||
|
|
||||||
|
const suffixOptions = [
|
||||||
|
{
|
||||||
|
key: "refreshCurrent",
|
||||||
|
label: "刷新当前",
|
||||||
|
|
||||||
|
icon: () => h(IconamoonSynchronize),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "closeCurrent",
|
||||||
|
label: "关闭当前",
|
||||||
|
icon: () => h(CarbonSubtractAlt),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "closeOther",
|
||||||
|
label: "关闭其他",
|
||||||
|
|
||||||
|
icon: () => h(MaterialSymbolsCancelOutline),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "closeAll",
|
||||||
|
label: "关闭所有",
|
||||||
|
icon: () => h(CodiconCloseAll),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const dropdownSelect = (key: string) => {
|
||||||
|
switch (key) {
|
||||||
|
case "refreshCurrent":
|
||||||
|
refreshCurrentPage?.();
|
||||||
|
break;
|
||||||
|
case "closeCurrent":
|
||||||
|
if (route.meta.isFixed) {
|
||||||
|
message.warning("当前为固定tab,不可关闭");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tabStore.closeTab(route.fullPath);
|
||||||
|
break;
|
||||||
|
case "closeOther":
|
||||||
|
tabStore.closeMultipleTab(route.fullPath);
|
||||||
|
break;
|
||||||
|
case "closeAll":
|
||||||
|
tabStore.closeMultipleTab();
|
||||||
|
router.push(INDEX_ROUTER.path);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -1,147 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="tabbar">
|
|
||||||
<div class="tabbar-left">
|
|
||||||
<menu-unfold-outlined
|
|
||||||
class="trigger"
|
|
||||||
@click="changeIcon"
|
|
||||||
v-if="useSetting.collapsed"
|
|
||||||
/>
|
|
||||||
<menu-fold-outlined class="trigger" v-else @click="changeIcon" />
|
|
||||||
<!-- 左侧面包屑 -->
|
|
||||||
<a-breadcrumb>
|
|
||||||
<template #separator><span>></span></template>
|
|
||||||
<a-breadcrumb-item
|
|
||||||
v-for="(item, index) in breadcrumbList"
|
|
||||||
:key="item.path"
|
|
||||||
v-show="item.name"
|
|
||||||
>
|
|
||||||
{{ item.name }}</a-breadcrumb-item
|
|
||||||
>
|
|
||||||
</a-breadcrumb>
|
|
||||||
</div>
|
|
||||||
<div class="tabbar-right">
|
|
||||||
<a-button style="margin-right: 8px" shape="circle" @click="refreshButton"
|
|
||||||
><sync-outlined
|
|
||||||
/></a-button>
|
|
||||||
<a-button style="margin-right: 8px" shape="circle" @click="fullScreen"
|
|
||||||
><ExpandOutlined
|
|
||||||
/></a-button>
|
|
||||||
<a-button style="margin-right: 8px" shape="circle"
|
|
||||||
><SettingOutlined
|
|
||||||
/></a-button>
|
|
||||||
<a-avatar src="https://www.antdv.com/assets/logo.1ef800a8.svg" />
|
|
||||||
<a-dropdown v-model:open="visible">
|
|
||||||
<a class="ant-dropdown-link" @click.prevent>
|
|
||||||
admin
|
|
||||||
<DownOutlined />
|
|
||||||
</a>
|
|
||||||
<template #overlay>
|
|
||||||
<a-menu @click="handleMenuClick">
|
|
||||||
<a-menu-item key="1">退出</a-menu-item>
|
|
||||||
<a-menu-item key="2">个人中心</a-menu-item>
|
|
||||||
<a-menu-item key="3">修改密码</a-menu-item>
|
|
||||||
</a-menu>
|
|
||||||
</template>
|
|
||||||
</a-dropdown>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref, computed } from "vue";
|
|
||||||
import {
|
|
||||||
MenuFoldOutlined,
|
|
||||||
MenuUnfoldOutlined,
|
|
||||||
SyncOutlined,
|
|
||||||
ExpandOutlined,
|
|
||||||
SettingOutlined,
|
|
||||||
DownOutlined,
|
|
||||||
} from "@ant-design/icons-vue";
|
|
||||||
import type { MenuProps } from "ant-design-vue";
|
|
||||||
import { useUserSetting } from "@/stores/modules/setting";
|
|
||||||
import { useRoute, useRouter } from "vue-router";
|
|
||||||
import { useUserStore } from "@/stores/modules/userStore";
|
|
||||||
import router from "@/router";
|
|
||||||
const userStore = useUserStore();
|
|
||||||
const useSetting = useUserSetting();
|
|
||||||
const $Route = useRoute();
|
|
||||||
const $Router = useRouter();
|
|
||||||
|
|
||||||
const visible = ref(false);
|
|
||||||
const handleMenuClick: MenuProps["onClick"] = (e) => {
|
|
||||||
if (e.key === "1") {
|
|
||||||
// 退出登录
|
|
||||||
userStore.deleteToken();
|
|
||||||
$Router.push({ path: "/login", query: { redirect: $Route.path } });
|
|
||||||
} else if (e.key === "3") {
|
|
||||||
visible.value = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const changeIcon = () => {
|
|
||||||
// 保存在pinia
|
|
||||||
useSetting.toggleCollapsed();
|
|
||||||
};
|
|
||||||
|
|
||||||
// 全局刷新
|
|
||||||
const refreshButton = () => {
|
|
||||||
useSetting.refreshPage();
|
|
||||||
};
|
|
||||||
|
|
||||||
// 全屏;
|
|
||||||
const fullScreen = () => {
|
|
||||||
let full = document.fullscreenElement; //获取状态
|
|
||||||
if (!full) {
|
|
||||||
document.documentElement.requestFullscreen();
|
|
||||||
} else {
|
|
||||||
document.exitFullscreen();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const breadcrumbList: any = computed(() => {
|
|
||||||
return $Route.matched.map((item) => ({
|
|
||||||
path: item.path,
|
|
||||||
name: item.name,
|
|
||||||
children: item.children || [],
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.tabbar {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
background-color: #fff;
|
|
||||||
|
|
||||||
.tabbar-left {
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
.trigger {
|
|
||||||
font-size: 18px;
|
|
||||||
padding: 0 24px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: color 0.3s;
|
|
||||||
border-left: 1px solid #ccc;
|
|
||||||
}
|
|
||||||
.breadcrumbItem {
|
|
||||||
color: rgba(0, 0, 0, 0.45);
|
|
||||||
transition: color 0.2s;
|
|
||||||
padding: 0 4px;
|
|
||||||
border-radius: 4px;
|
|
||||||
height: 22px;
|
|
||||||
display: inline-block;
|
|
||||||
margin-inline: -4px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.tabbar-right {
|
|
||||||
padding: 0 24px;
|
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
.ant-dropdown-link {
|
|
||||||
color: rgba(0, 0, 0, 0.45);
|
|
||||||
margin-left: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
<template>
|
||||||
|
<TablePro ref="tableRef" :request-api="reqApi" :search-form-options="searchFormOptions" :columns="columns" :isPageTable="false" :pagination="pa" :remote="false">
|
||||||
|
<template #selectionExtra>
|
||||||
|
<n-button size="small" text @click="logRows">123</n-button>
|
||||||
|
</template>
|
||||||
|
</TablePro>
|
||||||
|
</template>
|
||||||
|
<script setup lang="tsx">
|
||||||
|
import api from '@/axios'
|
||||||
|
import type { TableProInst, TableProProps } from '@/components/tablePro/src/interface'
|
||||||
|
import TablePro from '@/components/tablePro/src/TablePro.vue'
|
||||||
|
import { reactive, ref, useTemplateRef } from 'vue'
|
||||||
|
|
||||||
|
type TableType = TableProProps<any, any>
|
||||||
|
|
||||||
|
const tableRef = useTemplateRef<TableProInst>('tableRef')
|
||||||
|
const logRows = () => {
|
||||||
|
console.log(tableRef.value)
|
||||||
|
console.log(tableRef.value?.selectRows)
|
||||||
|
console.log(tableRef.value?.selectKeys)
|
||||||
|
}
|
||||||
|
const reqApi: TableType['requestApi'] = (params) => {
|
||||||
|
|
||||||
|
const pa = {
|
||||||
|
params: params,
|
||||||
|
page: {
|
||||||
|
current: 1,
|
||||||
|
size: 100,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return api.post('/common/pager', pa)
|
||||||
|
}
|
||||||
|
|
||||||
|
const pa = reactive({
|
||||||
|
page: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
onUpdatePage: (n: number) => {
|
||||||
|
pa.page = n
|
||||||
|
},
|
||||||
|
onUpdatePageSize: (s: number) => {
|
||||||
|
pa.pageSize = s
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const searchFormOptions = reactive<TableType['searchFormOptions']>({
|
||||||
|
projectName: {
|
||||||
|
type: 'input',
|
||||||
|
label: '名字',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const columns = ref<TableType['columns']>([
|
||||||
|
{
|
||||||
|
type: 'selection',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'name',
|
||||||
|
title: '名字',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'address',
|
||||||
|
title: '地址',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
</script>
|
||||||
|
<style scoped></style>
|
|
@ -0,0 +1,53 @@
|
||||||
|
<template>
|
||||||
|
<TablePro
|
||||||
|
ref="tableRef"
|
||||||
|
:request-api="reqApi"
|
||||||
|
:search-form-options="searchFormOptions"
|
||||||
|
:columns="columns"
|
||||||
|
>
|
||||||
|
<template #selectionExtra>
|
||||||
|
<n-button
|
||||||
|
size="small"
|
||||||
|
text
|
||||||
|
@click="logRows"
|
||||||
|
>
|
||||||
|
123
|
||||||
|
</n-button>
|
||||||
|
</template>
|
||||||
|
</TablePro>
|
||||||
|
</template>
|
||||||
|
<script setup lang="tsx">
|
||||||
|
import api from "@/axios";
|
||||||
|
import type {
|
||||||
|
TableProInst,
|
||||||
|
TableProProps,
|
||||||
|
} from "@/components/tablePro/src/interface";
|
||||||
|
import TablePro from "@/components/tablePro/src/TablePro.vue";
|
||||||
|
import { reactive, ref, useTemplateRef } from "vue";
|
||||||
|
|
||||||
|
type TableType = TableProProps<any, any>;
|
||||||
|
|
||||||
|
const tableRef = useTemplateRef<TableProInst>("tableRef");
|
||||||
|
const logRows = () => {
|
||||||
|
console.log(tableRef.value);
|
||||||
|
console.log(tableRef.value?.selectRows);
|
||||||
|
console.log(tableRef.value?.selectKeys);
|
||||||
|
};
|
||||||
|
const reqApi: TableType["requestApi"] = (params) => {
|
||||||
|
return api.post("/common/pager", params);
|
||||||
|
};
|
||||||
|
const searchFormOptions = reactive<TableType["searchFormOptions"]>({
|
||||||
|
projectName: {
|
||||||
|
type: "input",
|
||||||
|
label: "名字",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const columns = ref<TableType["columns"]>([
|
||||||
|
{
|
||||||
|
key: "name",
|
||||||
|
title: "菜单名字",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
</script>
|
||||||
|
<style scoped></style>
|
|
@ -0,0 +1,3 @@
|
||||||
|
export { tableProProps } from './src/TablePro'
|
||||||
|
export type { PageParams, Page, PageResult, TableProProps, TableProSlots, TableProInst } from './src/interface'
|
||||||
|
export { default as TablePro } from './src/TablePro.vue'
|