Compare commits
2 Commits
8716e35d55
...
2177aaf1e7
Author | SHA1 | Date |
---|---|---|
|
2177aaf1e7 | |
|
6f737791bd |
|
@ -1,12 +1,14 @@
|
|||
VITE_APP_NAME=保安管理
|
||||
VITE_APP_ENV=development
|
||||
VITE_APP_PORT=9527
|
||||
VITE_DROP_CONSOLE=false
|
||||
|
||||
# axios
|
||||
VITE_APP_BASE_API=/api
|
||||
VITE_APP_PROXY_URL=http://172.10.10.93:1233
|
||||
# VITE_APP_PROXY_URL=121321
|
||||
|
||||
#crypto js 前后端需保持一致
|
||||
# VITE_APP_CRYPTO_JS_SECRET_KEY=f0234d57c311beb2
|
||||
# VITE_APP_CRYPTO_JS_SECRET_IV=eb7905b31669ad1e
|
||||
VITE_APP_CRYPTO_JS_SECRET_KEY=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCo58dwFmtTkiA4bj/FiBqlYsiKOugT/WCQRYFPY49A7y6VEswzMihgxSz0B/UDdekM9PDsKy06Gjy8BpWu+ikR1ms+/PbMJOmw5jGUeiswf8uqJDDquHG5oJJk0o7J8/1JvzmpbN/Ctjcm2yUscTfG2WvvY0ViwnptYU7+ZkRcuQIDAQAB
|
||||
#jsencrypt 跟后端公钥保持一致
|
||||
VITE_APP_JS_ENCRYPT_PUBLIC_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_PORT=9528
|
||||
VITE_DROP_CONSOLE=true
|
||||
VITE_APP_NAME=保安管理
|
||||
VITE_APP_ENV=development
|
||||
VITE_APP_PORT=9527
|
||||
VITE_DROP_CONSOLE=false
|
||||
|
||||
# axios
|
||||
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 前后端需保持一致
|
||||
# VITE_APP_CRYPTO_JS_SECRET_KEY=f0234d57c311beb2
|
||||
# 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
|
|
@ -8,17 +8,32 @@ pnpm-debug.log*
|
|||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.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
|
||||
|
||||
- [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
|
||||
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)`
|
||||
2. Reload the VSCode window by running `Developer: Reload Window` from the command palette.
|
||||
See [Vite Configuration Reference](https://vite.dev/config/).
|
||||
|
||||
## Project Setup
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compile and Hot-Reload for Development
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Type-Check, Compile and Minify for Production
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
```
|
||||
|
|
18
index.html
|
@ -1,4 +1,21 @@
|
|||
<!DOCTYPE html>
|
||||
<<<<<<< HEAD
|
||||
<html lang="">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>保安管理系统</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
=======
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
|
@ -11,3 +28,4 @@
|
|||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
>>>>>>> 8716e35d55875b7e8377cae748005c95a6fe60c2
|
||||
|
|
58
package.json
|
@ -1,24 +1,36 @@
|
|||
{
|
||||
"name": "web",
|
||||
"private": true,
|
||||
"name": "management",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --mode development",
|
||||
"pro": "vite --mode production",
|
||||
"build": "vite build --mode production",
|
||||
"preview": "vite preview"
|
||||
"local": "vite --mode locality",
|
||||
"build": "run-p type-check \"build-only {@}\" --",
|
||||
"preview": "vite preview",
|
||||
"build-only": "vite build",
|
||||
"type-check": "vue-tsc --build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@amap/amap-jsapi-loader": "^1.0.1",
|
||||
"@types/node": "^20.5.0",
|
||||
"@vitejs/plugin-vue-jsx": "^3.0.1",
|
||||
"ant-design-vue": "4.x",
|
||||
"axios": "^1.4.0",
|
||||
"crypto-js": "^4.1.1",
|
||||
"echarts": "^5.4.3",
|
||||
"@vueuse/core": "^13.1.0",
|
||||
"animate.css": "^4.1.1",
|
||||
"axios": "^1.8.1",
|
||||
"dayjs": "^1.11.13",
|
||||
"event-source-polyfill": "^1.0.31",
|
||||
"js-base64": "^3.7.7",
|
||||
"jsencrypt": "^3.3.2",
|
||||
"lodash-es": "^4.17.21",
|
||||
<<<<<<< HEAD
|
||||
"pinia": "^3.0.1",
|
||||
"pinia-plugin-persistedstate": "^4.2.0",
|
||||
"sass": "^1.85.1",
|
||||
"terser": "^5.39.0",
|
||||
"unplugin-auto-import": "^19.1.1",
|
||||
"unplugin-vue-components": "^28.4.1",
|
||||
"vue": "^3.5.13",
|
||||
"vue-router": "^4.5.0",
|
||||
"naive-ui": "^2.41.0"
|
||||
=======
|
||||
"nprogress": "^0.2.0",
|
||||
"pinia": "^2.1.6",
|
||||
"pinia-plugin-persistedstate": "^3.2.0",
|
||||
|
@ -26,16 +38,20 @@
|
|||
"terser": "^5.19.2",
|
||||
"vue": "^3.3.4",
|
||||
"vue-router": "4"
|
||||
>>>>>>> 8716e35d55875b7e8377cae748005c95a6fe60c2
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/crypto-js": "^4.1.1",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/nprogress": "^0.2.3",
|
||||
"@vitejs/plugin-vue": "^4.2.3",
|
||||
"less": "^4.2.0",
|
||||
"sass": "^1.65.1",
|
||||
"typescript": "^5.0.2",
|
||||
"vite": "^4.4.5",
|
||||
"vue-tsc": "^1.8.5"
|
||||
"@tsconfig/node22": "^22.0.0",
|
||||
"@types/event-source-polyfill": "^1.0.5",
|
||||
"@types/node": "^22.13.4",
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
"@vitejs/plugin-vue-jsx": "^4.1.1",
|
||||
"@vue/tsconfig": "^0.7.0",
|
||||
"npm-run-all2": "^7.0.2",
|
||||
"rollup-plugin-visualizer": "^5.14.0",
|
||||
"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 |
32
src/App.vue
|
@ -1,13 +1,25 @@
|
|||
<template>
|
||||
<!-- <a-config-provider> -->
|
||||
<router-view></router-view>
|
||||
<!-- </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>
|
||||
</n-dialog-provider>
|
||||
</n-message-provider>
|
||||
</n-modal-provider>
|
||||
</n-notification-provider>
|
||||
</n-loading-bar-provider>
|
||||
</n-config-provider>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<style lang="scss">
|
||||
body {
|
||||
font-family: AliBaBaPuHuTi, serif;
|
||||
}
|
||||
</style>
|
||||
<script setup lang="ts">
|
||||
import { useSettingsStore } from "@/stores/settings";
|
||||
const settingsStore = useSettingsStore();
|
||||
import { dateZhCN, zhCN } from "naive-ui";
|
||||
</script>
|
||||
|
|
|
@ -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 */
|
||||
.flx-center {
|
||||
|
||||
.flex-center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.flx-justify-between {
|
||||
|
||||
.flex-end {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.flex-justify-between {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.flx-align-center {
|
||||
|
||||
.flex-align-center {
|
||||
display: flex;
|
||||
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::after {
|
||||
display: block;
|
||||
|
@ -49,11 +81,13 @@
|
|||
.fade-transform-enter-active {
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.fade-transform-enter-from {
|
||||
opacity: 0;
|
||||
transition: all 0.2s;
|
||||
transform: translateX(-30px);
|
||||
}
|
||||
|
||||
.fade-transform-leave-to {
|
||||
opacity: 0;
|
||||
transition: all 0.2s;
|
||||
|
@ -64,34 +98,13 @@
|
|||
.breadcrumb-enter-active {
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.breadcrumb-enter-from,
|
||||
.breadcrumb-leave-active {
|
||||
opacity: 0;
|
||||
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 {
|
||||
.mt#{$i} {
|
||||
|
@ -429,3 +442,46 @@
|
|||
padding-top: 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, {
|
||||
AxiosError,
|
||||
AxiosInstance,
|
||||
AxiosRequestConfig,
|
||||
AxiosResponse,
|
||||
InternalAxiosRequestConfig,
|
||||
type AxiosInstance,
|
||||
type AxiosRequestConfig,
|
||||
type AxiosResponse,
|
||||
} from "axios";
|
||||
import { message } from "ant-design-vue";
|
||||
import { useUserStore } from "@/stores/modules/userStore";
|
||||
import { CLIENT_TYPE } from "@/config/constant";
|
||||
export interface JsonResult<T> {
|
||||
code: number;
|
||||
message: string;
|
||||
data?: T;
|
||||
}
|
||||
|
||||
import router from "@/router";
|
||||
import Loading from "@/shared/classes/Loading";
|
||||
import { message } from "@/utils";
|
||||
import { CLIENT_TYPE, LOGIN_ROUTER } from "@/config/constant";
|
||||
import { useUserStore } from "@/stores/user";
|
||||
const axiosConfig: AxiosRequestConfig = {
|
||||
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: "请求超时......",
|
||||
};
|
||||
|
||||
|
@ -27,30 +23,49 @@ class RequestHttp {
|
|||
this.service = axios.create(config);
|
||||
//1.添加请求拦截
|
||||
this.service.interceptors.request.use(
|
||||
(config: InternalAxiosRequestConfig) => {
|
||||
//默认带上用户token
|
||||
config.headers.set("token", useUserStore().userInfo?.tokenValue);
|
||||
(config) => {
|
||||
const userStore = useUserStore();
|
||||
//请求必须带上当前客户端类型
|
||||
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;
|
||||
},
|
||||
(error: AxiosError): Promise<string> => {
|
||||
message.error(error.message).then((r) => {});
|
||||
async (error: AxiosError): Promise<string> => {
|
||||
Loading.close();
|
||||
message.error(error.message);
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
//2.添加响应拦截
|
||||
this.service.interceptors.response.use(
|
||||
(response: AxiosResponse): Promise<any> => {
|
||||
async (response: AxiosResponse): Promise<any> => {
|
||||
Loading.close();
|
||||
const jsonResult: JsonResult<unknown> = response.data;
|
||||
if (jsonResult.code !== 200) {
|
||||
//todo 一些特定的错误需要重新登录 这里暂时没处理
|
||||
message.error(jsonResult.message).then((r) => {});
|
||||
if (jsonResult && jsonResult.code !== 200) {
|
||||
//todo 一些特定的错误需要重新登录
|
||||
if ([-1].includes(jsonResult.code)) {
|
||||
//清除登录信息
|
||||
useUserStore().resetUserInfo();
|
||||
//跳转登录页
|
||||
await router.push({
|
||||
path: LOGIN_ROUTER.path,
|
||||
});
|
||||
}
|
||||
message.error(jsonResult.message);
|
||||
return Promise.reject(jsonResult);
|
||||
}
|
||||
return Promise.resolve(jsonResult);
|
||||
},
|
||||
(error: AxiosError): Promise<string> => {
|
||||
message.error(error.message).then((r) => {});
|
||||
async (error: AxiosError): Promise<string> => {
|
||||
Loading.close();
|
||||
message.error(error.message);
|
||||
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>
|
||||
<div class="not-container">
|
||||
<img src="@/assets/images/404.png" class="not-img" alt="404" />
|
||||
<div class="not-detail">
|
||||
<h2>404</h2>
|
||||
<h4>抱歉,您访问的页面不存在~🤷♂️🤷♀️</h4>
|
||||
<!-- <a-button type="primary" @click="router.push(INDEX_URL)"
|
||||
>返回首页</a-button -->
|
||||
</div>
|
||||
<div
|
||||
style="
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
"
|
||||
>
|
||||
<n-result
|
||||
status="404"
|
||||
title="404 资源不存在"
|
||||
description="生活总归带点荒谬"
|
||||
>
|
||||
<template #footer>
|
||||
<n-button @click.prevent="toHome">返回主页</n-button>
|
||||
</template>
|
||||
</n-result>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
const router = useRouter();
|
||||
import { useRouter } from "vue-router";
|
||||
const router = useRouter();
|
||||
function toHome() {
|
||||
router.push("/");
|
||||
}
|
||||
</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'
|