main
haha 2024-05-21 01:41:47 +08:00
parent f1a43a41da
commit 2e176e5be5
203 changed files with 16442 additions and 0 deletions

15
.editorconfig 100644
View File

@ -0,0 +1,15 @@
# http://editorconfig.org
root = true
# 表示所有文件适用
[*]
charset = utf-8 # 设置文件字符集为 utf-8
end_of_line = lf # 控制换行类型(lf | cr | crlf)
indent_style = space # 缩进风格tab | space
indent_size = 2 # 缩进大小
insert_final_newline = true # 始终在文件末尾插入一个新行
# 表示仅 md 文件适用以下规则
[*.md]
max_line_length = off # 关闭最大行长度限制
trim_trailing_whitespace = false # 关闭末尾空格修剪

13
.env.development 100644
View File

@ -0,0 +1,13 @@
# 应用端口
VITE_APP_PORT = 3000
# 代理前缀
VITE_APP_BASE_API = '/dev-api'
VITE_APP_API_URL = http://vapi.youlai.tech
# 线上接口地址
# VITE_APP_API_URL = http://62.234.3.186
# 开发接口地址
# VITE_APP_API_URL = http://localhost:8989
# 是否启用 Mock 服务
VITE_MOCK_DEV_SERVER = false

3
.env.production 100644
View File

@ -0,0 +1,3 @@
# 代理前缀
VITE_APP_BASE_API = '/dev-api'

14
.eslintignore 100644
View File

@ -0,0 +1,14 @@
dist
node_modules
public
.husky
.vscode
.idea
*.sh
*.md
src/assets
.eslintrc.cjs
.prettierrc.cjs
.stylelintrc.cjs

View File

@ -0,0 +1,284 @@
{
"globals": {
"Component": true,
"ComponentPublicInstance": true,
"ComputedRef": true,
"EffectScope": true,
"ElMessage": true,
"ElMessageBox": true,
"ElNotification": true,
"InjectionKey": true,
"PropType": true,
"Ref": true,
"VNode": true,
"asyncComputed": true,
"autoResetRef": true,
"computed": true,
"computedAsync": true,
"computedEager": true,
"computedInject": true,
"computedWithControl": true,
"controlledComputed": true,
"controlledRef": true,
"createApp": true,
"createEventHook": true,
"createGlobalState": true,
"createInjectionState": true,
"createReactiveFn": true,
"createReusableTemplate": true,
"createSharedComposable": true,
"createTemplatePromise": true,
"createUnrefFn": true,
"customRef": true,
"debouncedRef": true,
"debouncedWatch": true,
"defineAsyncComponent": true,
"defineComponent": true,
"eagerComputed": true,
"effectScope": true,
"extendRef": true,
"getCurrentInstance": true,
"getCurrentScope": true,
"h": true,
"ignorableWatch": true,
"inject": true,
"isDefined": true,
"isProxy": true,
"isReactive": true,
"isReadonly": true,
"isRef": true,
"makeDestructurable": true,
"markRaw": true,
"nextTick": true,
"onActivated": true,
"onBeforeMount": true,
"onBeforeUnmount": true,
"onBeforeUpdate": true,
"onClickOutside": true,
"onDeactivated": true,
"onErrorCaptured": true,
"onKeyStroke": true,
"onLongPress": true,
"onMounted": true,
"onRenderTracked": true,
"onRenderTriggered": true,
"onScopeDispose": true,
"onServerPrefetch": true,
"onStartTyping": true,
"onUnmounted": true,
"onUpdated": true,
"pausableWatch": true,
"provide": true,
"reactify": true,
"reactifyObject": true,
"reactive": true,
"reactiveComputed": true,
"reactiveOmit": true,
"reactivePick": true,
"readonly": true,
"ref": true,
"refAutoReset": true,
"refDebounced": true,
"refDefault": true,
"refThrottled": true,
"refWithControl": true,
"resolveComponent": true,
"resolveRef": true,
"resolveUnref": true,
"shallowReactive": true,
"shallowReadonly": true,
"shallowRef": true,
"syncRef": true,
"syncRefs": true,
"templateRef": true,
"throttledRef": true,
"throttledWatch": true,
"toRaw": true,
"toReactive": true,
"toRef": true,
"toRefs": true,
"toValue": true,
"triggerRef": true,
"tryOnBeforeMount": true,
"tryOnBeforeUnmount": true,
"tryOnMounted": true,
"tryOnScopeDispose": true,
"tryOnUnmounted": true,
"unref": true,
"unrefElement": true,
"until": true,
"useActiveElement": true,
"useAnimate": true,
"useArrayDifference": true,
"useArrayEvery": true,
"useArrayFilter": true,
"useArrayFind": true,
"useArrayFindIndex": true,
"useArrayFindLast": true,
"useArrayIncludes": true,
"useArrayJoin": true,
"useArrayMap": true,
"useArrayReduce": true,
"useArraySome": true,
"useArrayUnique": true,
"useAsyncQueue": true,
"useAsyncState": true,
"useAttrs": true,
"useBase64": true,
"useBattery": true,
"useBluetooth": true,
"useBreakpoints": true,
"useBroadcastChannel": true,
"useBrowserLocation": true,
"useCached": true,
"useClipboard": true,
"useCloned": true,
"useColorMode": true,
"useConfirmDialog": true,
"useCounter": true,
"useCssModule": true,
"useCssVar": true,
"useCssVars": true,
"useCurrentElement": true,
"useCycleList": true,
"useDark": true,
"useDateFormat": true,
"useDebounce": true,
"useDebounceFn": true,
"useDebouncedRefHistory": true,
"useDeviceMotion": true,
"useDeviceOrientation": true,
"useDevicePixelRatio": true,
"useDevicesList": true,
"useDisplayMedia": true,
"useDocumentVisibility": true,
"useDraggable": true,
"useDropZone": true,
"useElementBounding": true,
"useElementByPoint": true,
"useElementHover": true,
"useElementSize": true,
"useElementVisibility": true,
"useEventBus": true,
"useEventListener": true,
"useEventSource": true,
"useEyeDropper": true,
"useFavicon": true,
"useFetch": true,
"useFileDialog": true,
"useFileSystemAccess": true,
"useFocus": true,
"useFocusWithin": true,
"useFps": true,
"useFullscreen": true,
"useGamepad": true,
"useGeolocation": true,
"useIdle": true,
"useImage": true,
"useInfiniteScroll": true,
"useIntersectionObserver": true,
"useInterval": true,
"useIntervalFn": true,
"useKeyModifier": true,
"useLastChanged": true,
"useLocalStorage": true,
"useMagicKeys": true,
"useManualRefHistory": true,
"useMediaControls": true,
"useMediaQuery": true,
"useMemoize": true,
"useMemory": true,
"useMounted": true,
"useMouse": true,
"useMouseInElement": true,
"useMousePressed": true,
"useMutationObserver": true,
"useNavigatorLanguage": true,
"useNetwork": true,
"useNow": true,
"useObjectUrl": true,
"useOffsetPagination": true,
"useOnline": true,
"usePageLeave": true,
"useParallax": true,
"useParentElement": true,
"usePerformanceObserver": true,
"usePermission": true,
"usePointer": true,
"usePointerLock": true,
"usePointerSwipe": true,
"usePreferredColorScheme": true,
"usePreferredContrast": true,
"usePreferredDark": true,
"usePreferredLanguages": true,
"usePreferredReducedMotion": true,
"usePrevious": true,
"useRafFn": true,
"useRefHistory": true,
"useResizeObserver": true,
"useScreenOrientation": true,
"useScreenSafeArea": true,
"useScriptTag": true,
"useScroll": true,
"useScrollLock": true,
"useSessionStorage": true,
"useShare": true,
"useSlots": true,
"useSorted": true,
"useSpeechRecognition": true,
"useSpeechSynthesis": true,
"useStepper": true,
"useStorage": true,
"useStorageAsync": true,
"useStyleTag": true,
"useSupported": true,
"useSwipe": true,
"useTemplateRefsList": true,
"useTextDirection": true,
"useTextSelection": true,
"useTextareaAutosize": true,
"useThrottle": true,
"useThrottleFn": true,
"useThrottledRefHistory": true,
"useTimeAgo": true,
"useTimeout": true,
"useTimeoutFn": true,
"useTimeoutPoll": true,
"useTimestamp": true,
"useTitle": true,
"useToNumber": true,
"useToString": true,
"useToggle": true,
"useTransition": true,
"useUrlSearchParams": true,
"useUserMedia": true,
"useVModel": true,
"useVModels": true,
"useVibrate": true,
"useVirtualList": true,
"useWakeLock": true,
"useWebNotification": true,
"useWebSocket": true,
"useWebWorker": true,
"useWebWorkerFn": true,
"useWindowFocus": true,
"useWindowScroll": true,
"useWindowSize": true,
"watch": true,
"watchArray": true,
"watchAtMost": true,
"watchDebounced": true,
"watchDeep": true,
"watchEffect": true,
"watchIgnorable": true,
"watchImmediate": true,
"watchOnce": true,
"watchPausable": true,
"watchPostEffect": true,
"watchSyncEffect": true,
"watchThrottled": true,
"watchTriggerable": true,
"watchWithFilter": true,
"whenever": true
}
}

88
.eslintrc.cjs 100644
View File

@ -0,0 +1,88 @@
module.exports = {
root: true,
env: {
browser: true,
es2021: true,
node: true,
},
parser: "vue-eslint-parser",
extends: [
// https://eslint.vuejs.org/user-guide/#usage
"plugin:vue/vue3-recommended",
"./.eslintrc-auto-import.json",
"prettier",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended",
],
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
parser: "@typescript-eslint/parser",
project: "./tsconfig.*?.json",
createDefaultProgram: false,
extraFileExtensions: [".vue"],
},
plugins: ["vue", "@typescript-eslint"],
rules: {
// https://eslint.vuejs.org/rules/#priority-a-essential-error-prevention
"vue/multi-word-component-names": "off",
"vue/no-v-model-argument": "off",
"vue/script-setup-uses-vars": "error",
"vue/no-reserved-component-names": "off",
"vue/custom-event-name-casing": "off",
"vue/attributes-order": "off",
"vue/one-component-per-file": "off",
"vue/html-closing-bracket-newline": "off",
"vue/max-attributes-per-line": "off",
"vue/multiline-html-element-content-newline": "off",
"vue/singleline-html-element-content-newline": "off",
"vue/attribute-hyphenation": "off",
"vue/require-default-prop": "off",
"vue/require-explicit-emits": "off",
"vue/html-self-closing": [
"error",
{
html: {
void: "always",
normal: "never",
component: "always",
},
svg: "always",
math: "always",
},
],
"@typescript-eslint/no-empty-function": "off", // 关闭空方法检查
"@typescript-eslint/no-explicit-any": "off", // 关闭any类型的警告
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/ban-ts-ignore": "off",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/ban-types": "off",
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-var-requires": "off",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-use-before-define": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-unused-vars": "off",
"prettier/prettier": [
"error",
{
useTabs: false, // 不使用制表符
},
],
},
// eslint不能对html文件生效
overrides: [
{
files: ["*.html"],
processor: "vue/.vue",
},
],
// https://eslint.org/docs/latest/use/configure/language-options#specifying-globals
globals: {
OptionType: "readonly",
},
};

18
.gitignore vendored 100644
View File

@ -0,0 +1,18 @@
node_modules
.DS_Store
dist
dist-ssr
*.local
.history
# Editor directories and files
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.local
package-lock.json
pnpm-lock.yaml
stats.html

11
.prettierignore 100644
View File

@ -0,0 +1,11 @@
dist
node_modules
public
.husky
.vscode
.idea
*.sh
*.md
src/assets
stats.html

46
.prettierrc.cjs 100644
View File

@ -0,0 +1,46 @@
module.exports = {
// (x)=>{},单个参数箭头函数是否显示小括号。(always:始终显示;avoid:省略括号。默认:always)
arrowParens: "always",
// 开始标签的右尖括号是否跟随在最后一行属性末尾默认false
bracketSameLine: false,
// 对象字面量的括号之间打印空格 (true - Example: { foo: bar } ; false - Example: {foo:bar})
bracketSpacing: true,
// 是否格式化一些文件中被嵌入的代码片段的风格(auto|off;默认auto)
embeddedLanguageFormatting: "auto",
// 指定 HTML 文件的空格敏感度 (css|strict|ignore;默认css)
htmlWhitespaceSensitivity: "css",
// 当文件已经被 Prettier 格式化之后,是否会在文件顶部插入一个特殊的 @format 标记默认false
insertPragma: false,
// 在 JSX 中使用单引号替代双引号默认false
jsxSingleQuote: false,
// 每行最多字符数量,超出换行(默认80)
printWidth: 80,
// 超出打印宽度 (always | never | preserve )
proseWrap: "preserve",
// 对象属性是否使用引号(as-needed | consistent | preserve;默认as-needed:对象的属性需要加引号才添加;)
quoteProps: "as-needed",
// 是否只格式化在文件顶部包含特定注释(@prettier| @format)的文件默认false
requirePragma: false,
// 结尾添加分号
semi: true,
// 使用单引号 (true:单引号;false:双引号)
singleQuote: false,
// 缩进空格数默认2个空格
tabWidth: 2,
// 元素末尾是否加逗号默认es5: ES5中的 objects, arrays 等会添加逗号TypeScript 中的 type 后不加逗号
trailingComma: "es5",
// 指定缩进方式空格或tab默认false即使用空格
useTabs: false,
// vue 文件中是否缩进 <style> 和 <script> 标签,默认 false
vueIndentScriptAndStyle: false,
endOfLine: "auto",
overrides: [
{
files: "*.html",
options: {
parser: "html",
},
},
],
};

11
.stylelintignore 100644
View File

@ -0,0 +1,11 @@
dist
node_modules
public
.husky
.vscode
.idea
*.sh
*.md
src/assets
stats.html

51
.stylelintrc.cjs 100644
View File

@ -0,0 +1,51 @@
module.exports = {
// 继承推荐规范配置
extends: [
"stylelint-config-standard",
"stylelint-config-recommended-scss",
"stylelint-config-recommended-vue/scss",
"stylelint-config-html/vue",
"stylelint-config-recess-order",
],
// 指定不同文件对应的解析器
overrides: [
{
files: ["**/*.{vue,html}"],
customSyntax: "postcss-html",
},
{
files: ["**/*.{css,scss}"],
customSyntax: "postcss-scss",
},
],
// 自定义规则
rules: {
"import-notation": "string", // 指定导入CSS文件的方式("string"|"url")
"selector-class-pattern": null, // 选择器类名命名规则
"custom-property-pattern": null, // 自定义属性命名规则
"keyframes-name-pattern": null, // 动画帧节点样式命名规则
"no-descending-specificity": null, // 允许无降序特异性
"no-empty-source": null, // 允许空样式
// 允许 global 、export 、deep伪类
"selector-pseudo-class-no-unknown": [
true,
{
ignorePseudoClasses: ["global", "export", "deep"],
},
],
// 允许未知属性
"property-no-unknown": [
true,
{
ignoreProperties: [],
},
],
// 允许未知规则
"at-rule-no-unknown": [
true,
{
ignoreAtRules: ["apply", "use"],
},
],
},
};

3
.vscode/extensions.json vendored 100644
View File

@ -0,0 +1,3 @@
{
"recommendations": ["Vue.volar"]
}

View File

@ -0,0 +1,93 @@
module.exports = {
// 继承的规则
extends: ["@commitlint/config-conventional"],
// 自定义规则
rules: {
// @see https://commitlint.js.org/#/reference-rules
// 提交类型枚举git提交type必须是以下类型
"type-enum": [
2,
"always",
[
"feat", // 新增功能
"fix", // 修复缺陷
"docs", // 文档变更
"style", // 代码格式(不影响功能,例如空格、分号等格式修正)
"refactor", // 代码重构(不包括 bug 修复、功能新增)
"perf", // 性能优化
"test", // 添加疏漏测试或已有测试改动
"build", // 构建流程、外部依赖变更(如升级 npm 包、修改 webpack 配置等)
"ci", // 修改 CI 配置、脚本
"revert", // 回滚 commit
"chore", // 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)
],
],
"subject-case": [0], // subject大小写不做校验
},
prompt: {
messages: {
type: "选择你要提交的类型 :",
scope: "选择一个提交范围(可选):",
customScope: "请输入自定义的提交范围 :",
subject: "填写简短精炼的变更描述 :\n",
body: '填写更加详细的变更描述(可选)。使用 "|" 换行 :\n',
breaking: '列举非兼容性重大的变更(可选)。使用 "|" 换行 :\n',
footerPrefixesSelect: "选择关联issue前缀可选:",
customFooterPrefix: "输入自定义issue前缀 :",
footer: "列举关联issue (可选) 例如: #31, #I3244 :\n",
generatingByAI: "正在通过 AI 生成你的提交简短描述...",
generatedSelectByAI: "选择一个 AI 生成的简短描述:",
confirmCommit: "是否提交或修改commit ?",
},
// prettier-ignore
types: [
{ value: "feat", name: "特性: ✨ 新增功能", emoji: ":sparkles:" },
{ value: "fix", name: "修复: 🐛 修复缺陷", emoji: ":bug:" },
{ value: "docs", name: "文档: 📝 文档变更(更新README文件或者注释)", emoji: ":memo:" },
{ value: "style", name: "格式: 🌈 代码格式(空格、格式化、缺失的分号等)", emoji: ":lipstick:" },
{ value: "refactor", name: "重构: 🔄 代码重构(不修复错误也不添加特性的代码更改)", emoji: ":recycle:" },
{ value: "perf", name: "性能: 🚀 性能优化", emoji: ":zap:" },
{ value: "test", name: "测试: 🧪 添加疏漏测试或已有测试改动", emoji: ":white_check_mark:"},
{ value: "build", name: "构建: 📦️ 构建流程、外部依赖变更(如升级 npm 包、修改 vite 配置等)", emoji: ":package:"},
{ value: "ci", name: "集成: ⚙️ 修改 CI 配置、脚本", emoji: ":ferris_wheel:"},
{ value: "revert", name: "回退: ↩️ 回滚 commit",emoji: ":rewind:"},
{ value: "chore", name: "其他: 🛠️ 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)", emoji: ":hammer:"},
],
useEmoji: true,
emojiAlign: "center",
useAI: false,
aiNumber: 1,
themeColorCode: "",
scopes: [],
allowCustomScopes: true,
allowEmptyScopes: true,
customScopesAlign: "bottom",
customScopesAlias: "custom",
emptyScopesAlias: "empty",
upperCaseSubject: false,
markBreakingChangeMode: false,
allowBreakingChanges: ["feat", "fix"],
breaklineNumber: 100,
breaklineChar: "|",
skipQuestions: [],
issuePrefixes: [
{ value: "closed", name: "closed: ISSUES has been processed" },
],
customIssuePrefixAlign: "top",
emptyIssuePrefixAlias: "skip",
customIssuePrefixAlias: "custom",
allowCustomIssuePrefix: true,
allowEmptyIssuePrefix: true,
confirmColorize: true,
maxHeaderLength: Infinity,
maxSubjectLength: Infinity,
minSubjectLength: 0,
scopeOverrides: undefined,
defaultBody: "",
defaultIssues: "",
defaultScope: "",
defaultSubject: "",
},
};

66
index.html 100644
View File

@ -0,0 +1,66 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta
name="description"
content="Vue3 + Vite5 + TypeScript5 + Element-Plus 的后台管理模板配套接口文档和后端源码vue-element-admin 的 Vue3 版本"
/>
<meta
name="keywords"
content="vue,element-plus,typescript,vue-element-admin,vue3-element-admin"
/>
<title>vue3-element-admin</title>
</head>
<body>
<div id="app">
<div class="loader"></div>
</div>
</body>
<script type="module" src="/src/main.ts"></script>
<style>
html,
body,
#app {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
.loader {
position: relative;
width: 40px;
aspect-ratio: 0.577;
overflow: hidden;
clip-path: polygon(0 0, 100% 100%, 0 100%, 100% 0);
animation: l19 2s infinite linear;
}
.loader::before {
position: absolute;
inset: -150%;
content: "";
background: repeating-conic-gradient(
from 30deg,
#ffabab 0 60deg,
#abe4ff 0 120deg,
#ff7373 0 180deg
);
animation: inherit;
animation-direction: reverse;
}
@keyframes l19 {
100% {
transform: rotate(360deg);
}
}
</style>
</html>

125
package.json 100644
View File

@ -0,0 +1,125 @@
{
"name": "aimanage",
"private": true,
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc --noEmit & vite build",
"preview": "vite preview",
"build-only": "vite build",
"type-check": "vue-tsc --noEmit",
"lint:eslint": "eslint --fix --ext .ts,.js,.vue ./src ",
"lint:prettier": "prettier --write \"**/*.{js,cjs,ts,json,tsx,css,less,scss,vue,html,md}\"",
"lint:stylelint": "stylelint \"**/*.{css,scss,vue}\" --fix",
"lint:lint-staged": "lint-staged",
"prepare": "husky",
"commit": "git-cz"
},
"config": {
"commitizen": {
"path": "node_modules/cz-git"
}
},
"lint-staged": {
"*.{js,ts}": [
"eslint --fix",
"prettier --write"
],
"*.{cjs,json}": [
"prettier --write"
],
"*.{vue,html}": [
"eslint --fix",
"prettier --write",
"stylelint --fix"
],
"*.{scss,css}": [
"stylelint --fix",
"prettier --write"
],
"*.md": [
"prettier --write"
]
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.1",
"@vueuse/core": "^10.9.0",
"@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "5.1.10",
"animate.css": "^4.1.1",
"axios": "^1.6.8",
"color": "^4.2.3",
"echarts": "^5.5.0",
"element-plus": "^2.7.2",
"lodash-es": "^4.17.21",
"net": "^1.0.2",
"nprogress": "^0.2.0",
"path-browserify": "^1.0.1",
"path-to-regexp": "^6.2.2",
"pinia": "^2.1.7",
"sockjs-client": "1.6.1",
"sortablejs": "^1.15.2",
"stompjs": "^2.3.3",
"vue": "^3.4.26",
"vue-i18n": "9.9.1",
"vue-router": "^4.3.2",
"xlsx": "^0.18.5"
},
"devDependencies": {
"@commitlint/cli": "^18.6.1",
"@commitlint/config-conventional": "^18.6.3",
"@iconify-json/ep": "^1.1.15",
"@types/color": "^3.0.6",
"@types/lodash": "^4.17.1",
"@types/node": "^20.12.8",
"@types/nprogress": "^0.2.3",
"@types/path-browserify": "^1.0.2",
"@types/sockjs-client": "^1.5.4",
"@types/sortablejs": "^1.15.8",
"@types/stompjs": "^2.3.9",
"@typescript-eslint/eslint-plugin": "^7.8.0",
"@typescript-eslint/parser": "^7.8.0",
"@vitejs/plugin-vue": "^5.0.4",
"@vitejs/plugin-vue-jsx": "^3.1.0",
"autoprefixer": "^10.4.19",
"commitizen": "^4.3.0",
"cz-git": "^1.9.1",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-vue": "^9.25.0",
"fast-glob": "^3.3.2",
"husky": "^9.0.11",
"lint-staged": "^15.2.2",
"postcss": "^8.4.38",
"postcss-html": "^1.6.0",
"postcss-scss": "^4.0.9",
"prettier": "^3.2.5",
"sass": "^1.76.0",
"stylelint": "^16.5.0",
"stylelint-config-html": "^1.1.0",
"stylelint-config-recess-order": "^4.6.0",
"stylelint-config-recommended-scss": "^14.0.0",
"stylelint-config-recommended-vue": "^1.5.0",
"stylelint-config-standard": "^36.0.0",
"terser": "^5.31.0",
"typescript": "^5.4.5",
"unocss": "^0.58.9",
"unplugin-auto-import": "^0.17.5",
"unplugin-icons": "^0.18.5",
"unplugin-vue-components": "^0.26.0",
"vite": "^5.2.11",
"vite-plugin-mock-dev-server": "^1.5.0",
"vite-plugin-svg-icons": "^2.0.1",
"vite-plugin-vue-devtools": "^7.1.3",
"vue-tsc": "^2.0.16"
},
"repository": "https://gitee.com/youlaiorg/vue3-element-admin.git",
"author": "有来开源组织",
"license": "MIT",
"engines": {
"node": ">=18.0.0"
}
}

1
public/vite.svg 100644
View File

@ -0,0 +1 @@
<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>

After

Width:  |  Height:  |  Size: 1.5 KiB

36
src/App.vue 100644
View File

@ -0,0 +1,36 @@
<template>
<el-config-provider :locale="locale" :size="size">
<!-- 开启水印 -->
<el-watermark
v-if="watermarkEnabled"
:font="{ color: fontColor }"
:content="defaultSettings.watermarkContent"
class="wh-full"
>
<router-view />
</el-watermark>
<!-- 关闭水印 -->
<router-view v-else />
</el-config-provider>
</template>
<script setup lang="ts">
import { useAppStore, useSettingsStore } from "@/store";
import defaultSettings from "@/settings";
import { ThemeEnum } from "@/enums/ThemeEnum";
import { SizeEnum } from "@/enums/SizeEnum";
const appStore = useAppStore();
const settingsStore = useSettingsStore();
const locale = computed(() => appStore.locale);
const size = computed(() => appStore.size as SizeEnum);
const watermarkEnabled = computed(() => settingsStore.watermarkEnabled);
// /
const fontColor = computed(() => {
return settingsStore.theme === ThemeEnum.DARK
? "rgba(255, 255, 255, .15)"
: "rgba(0, 0, 0, .15)";
});
</script>

View File

@ -0,0 +1,66 @@
import request from "@/utils/request";
import { CaptchaResult, LoginData, LoginResult } from "./model";
class AuthAPI {
/**
* API
*
* @param data {LoginData}
* @returns
*/
static login(data: LoginData) {
/*
const formData = new FormData();
formData.append("username", data.username);
formData.append("password", data.password);
formData.append("captchaKey", data.captchaKey || "");
formData.append("captchaCode", data.captchaCode || "");
return request<any, LoginResult>({
url: "/api/v1/auth/login",
method: "post",
data: formData,
headers: {
"Content-Type": "multipart/form-data",
},
});*/
return new Promise((resolve,reject)=>{
if(data.username!='admin' || data.password!='123456'){
reject("用户名或密码错误");
}else{
resolve( {
"accessToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsImRlcHRJZCI6MSwiZGF0YVNjb3BlIjoxLCJleHAiOjE3MTYyMjY0OTksInVzZXJJZCI6MiwiaWF0IjoxNzE2MjE5Mjk5LCJhdXRob3JpdGllcyI6WyJST0xFX0FETUlOIl0sImp0aSI6IjVmMWIwNTExNmZjZDQxZmE5YTVlNjU4ZjQ5YmRmNmEyIn0.BBCWh3-bffEtgT0zhxSQ0ncithh9iIGDtbg1DPN0TvA",
"tokenType": "Bearer",
"refreshToken": null,
"expires": null
});
}
});
}
/**
* API
*/
static logout() {
return new Promise(r=>{
r({});
});
/*
return request({
url: "/api/v1/auth/logout",
method: "delete",
});
*/
}
/**
*
*/
static getCaptcha() {
return request<any, CaptchaResult>({
url: "/api/v1/auth/captcha",
method: "get",
});
}
}
export default AuthAPI;

View File

@ -0,0 +1,59 @@
/**
*
*/
export interface LoginData {
/**
*
*/
username: string;
/**
*
*/
password: string;
/**
* key
*/
captchaKey?: string;
/**
*
*/
captchaCode?: string;
}
/**
*
*/
export interface LoginResult {
/**
* 访token
*/
accessToken?: string;
/**
* ()
*/
expires?: number;
/**
* token
*/
refreshToken?: string;
/**
* token
*/
tokenType?: string;
}
/**
*
*/
export interface CaptchaResult {
/**
* key
*/
captchaKey: string;
/**
* Base64
*/
captchaBase64: string;
}

View File

@ -0,0 +1,80 @@
import request from "@/utils/request";
import { DeptForm, DeptQuery, DeptVO } from "./model";
class DeptAPI {
/**
*
*
* @param queryParams
*/
static getList(queryParams?: DeptQuery) {
return request<any, DeptVO[]>({
url: "/api/v1/dept",
method: "get",
params: queryParams,
});
}
/**
*
*/
static getOptions() {
return request<any, OptionType[]>({
url: "/api/v1/dept/options",
method: "get",
});
}
/**
*
*
* @param id
*/
static getFormData(id: number) {
return request<any, DeptForm>({
url: "/api/v1/dept/" + id + "/form",
method: "get",
});
}
/**
*
*
* @param data
*/
static add(data: DeptForm) {
return request({
url: "/api/v1/dept",
method: "post",
data: data,
});
}
/**
*
*
* @param id
* @param data
*/
static update(id: number, data: DeptForm) {
return request({
url: "/api/v1/dept/" + id,
method: "put",
data: data,
});
}
/**
*
*
* @param ids
*/
static deleteByIds(ids: string) {
return request({
url: "/api/v1/dept/" + ids,
method: "delete",
});
}
}
export default DeptAPI;

View File

@ -0,0 +1,71 @@
/**
*
*/
export interface DeptQuery {
keywords?: string;
status?: number;
}
/**
*
*/
export interface DeptVO {
/**
*
*/
children?: DeptVO[];
/**
*
*/
createTime?: Date;
/**
* ID
*/
id?: number;
/**
*
*/
name?: string;
/**
* ID
*/
parentId?: number;
/**
*
*/
sort?: number;
/**
* (1:0:)
*/
status?: number;
/**
*
*/
updateTime?: Date;
}
/**
*
*/
export interface DeptForm {
/**
* ID()
*/
id?: number;
/**
*
*/
name?: string;
/**
* ID
*/
parentId: number;
/**
*
*/
sort?: number;
/**
* (1:0)
*/
status?: number;
}

View File

@ -0,0 +1,149 @@
import request from "@/utils/request";
import {
DictTypeQuery,
DictTypePageResult,
DictTypeForm,
DictQuery,
DictForm,
DictPageResult,
} from "./model";
class DictAPI {
/**
*
*
* @param queryParams
*/
static getDictTypePage(queryParams: DictTypeQuery) {
return request<any, DictTypePageResult>({
url: "/api/v1/dict/types/page",
method: "get",
params: queryParams,
});
}
/**
*
*
* @param id
*/
static getDictTypeForm(id: number) {
return request<any, ResponseData<DictTypeForm>>({
url: "/api/v1/dict/types/" + id + "/form",
method: "get",
});
}
/**
*
*
* @param data
*/
static addDictType(data: DictTypeForm) {
return request({
url: "/api/v1/dict/types",
method: "post",
data: data,
});
}
/**
*
*
* @param id
* @param data
*/
static updateDictType(id: number, data: DictTypeForm) {
return request({
url: "/api/v1/dict/types/" + id,
method: "put",
data: data,
});
}
/**
*
*/
static deleteDictTypes(ids: string) {
return request({
url: "/api/v1/dict/types/" + ids,
method: "delete",
});
}
/**
*
*
* @param typeCode
*/
static getDictOptions(typeCode: string) {
return request<any, OptionType[]>({
url: "/api/v1/dict/" + typeCode + "/options",
method: "get",
});
}
/**
*
*/
static getDictPage(queryParams: DictQuery) {
return request<any, DictPageResult>({
url: "/api/v1/dict/page",
method: "get",
params: queryParams,
});
}
/**
*
*
* @param id
*/
static getDictFormData(id: number) {
return request<any, DictForm>({
url: "/api/v1/dict/" + id + "/form",
method: "get",
});
}
/**
*
*
* @param data
*/
static addDict(data: DictForm) {
return request({
url: "/api/v1/dict",
method: "post",
data: data,
});
}
/**
*
*
* @param id
* @param data
*/
static updateDict(id: number, data: DictForm) {
return request({
url: "/api/v1/dict/" + id,
method: "put",
data: data,
});
}
/**
*
*
* @param ids ID(,)
*/
static deleteDictByIds(ids: string) {
return request({
url: "/api/v1/dict/" + ids,
method: "delete",
});
}
}
export default DictAPI;

View File

@ -0,0 +1,142 @@
/**
*
*/
export interface DictTypeQuery extends PageQuery {
/**
* (/)
*/
keywords?: string;
}
/**
*
*/
export interface DictTypePageVO {
/**
* ID
*/
id: number;
/**
*
*/
code: string;
/**
*
*/
name: string;
/**
* (1:;0:)
*/
status?: number;
/**
*
*/
remark?: string;
}
/**
*
*/
export type DictTypePageResult = PageResult<DictTypePageVO[]>;
/**
*
*/
export interface DictTypeForm {
/**
* ID
*/
id?: number;
/**
*
*/
name?: string;
/**
*
*/
code?: string;
/**
* 1:;0:
*/
status: number;
/**
*
*/
remark?: string;
}
/**
*
*/
export interface DictQuery extends PageQuery {
/**
*
*/
name?: string;
/**
*
*/
typeCode?: string;
}
/**
*
*/
export interface DictPageVO {
/**
* ID
*/
id?: number;
/**
*
*/
name?: string;
/**
* (1:;0:)
*/
status?: number;
/**
*
*/
value?: string;
}
/**
*
*/
export type DictPageResult = PageResult<DictPageVO[]>;
/**
*
*/
export interface DictForm {
/**
* ID
*/
id?: number;
/**
*
*/
name?: string;
/**
*
*/
sort?: number;
/**
* (1:;0:)
*/
status?: number;
/**
*
*/
typeCode?: string;
/**
*
*/
value?: string;
/**
*
*/
remark?: string;
}

View File

@ -0,0 +1,37 @@
import request from "@/utils/request";
import { FileInfo } from "./model";
class FileAPI {
/**
*
*
* @param file
*/
static upload(file: File) {
const formData = new FormData();
formData.append("file", file);
return request<any, FileInfo>({
url: "/api/v1/files",
method: "post",
data: formData,
headers: {
"Content-Type": "multipart/form-data",
},
});
}
/**
*
*
* @param filePath
*/
static deleteByPath(filePath?: string) {
return request({
url: "/api/v1/files",
method: "delete",
params: { filePath: filePath },
});
}
}
export default FileAPI;

View File

@ -0,0 +1,7 @@
/**
* API
*/
export interface FileInfo {
name: string;
url: string;
}

View File

@ -0,0 +1,382 @@
import request from "@/utils/request";
import { MenuQuery, MenuVO, MenuForm, RouteVO } from "./model";
class MenuAPI {
/**
*
*/
static getRoutes() {
return new Promise((resolve) => {
resolve([
{
path: "/split",
component: "Layout",
redirect: "/split/index",
name: "/split",
meta: {
title: "算法分割",
icon: "project",
hidden: false,
roles: ["GUEST", "ADMIN", "ADMIN6"],
alwaysShow: false,
},
children: [
{
path: "user",
component: "system/user/index",
name: "User",
meta: {
title: "算法分割1",
icon: "user",
hidden: false,
roles: ["ADMIN", "GUEST"],
keepAlive: true,
alwaysShow: false,
},
},
{
path: "role",
component: "system/role/index",
name: "Role",
meta: {
title: "算法分割2",
icon: "role",
hidden: false,
roles: ["ADMIN6", "GUEST", "ADMIN"],
keepAlive: true,
alwaysShow: false,
},
},
],
},
{
path: "/internetProtocol",
component: "Layout",
redirect: "/internetProtocol/index",
name: "/internetProtocol",
meta: {
title: "互联协议",
icon: "api",
hidden: false,
roles: ["GUEST", "ADMIN", "ADMIN6"],
alwaysShow: false,
},
children: [
{
path: "user",
component: "system/user/index",
name: "User",
meta: {
title: "互联协议1",
icon: "user",
hidden: false,
roles: ["ADMIN", "GUEST"],
keepAlive: true,
alwaysShow: false,
},
},
{
path: "role",
component: "system/role/index",
name: "Role",
meta: {
title: "互联协议2",
icon: "role",
hidden: false,
roles: ["ADMIN6", "GUEST", "ADMIN"],
keepAlive: true,
alwaysShow: false,
},
},
],
},
{
path: "/simulationEvaluation",
component: "Layout",
redirect: "/simulationEvaluation/index",
name: "/simulationEvaluation",
meta: {
title: "仿真评估",
icon: "client",
hidden: false,
roles: ["GUEST", "ADMIN", "ADMIN6"],
alwaysShow: false,
},
children: [
{
path: "user",
component: "system/user/index",
name: "User",
meta: {
title: "仿真评估1",
icon: "user",
hidden: false,
roles: ["ADMIN", "GUEST"],
keepAlive: true,
alwaysShow: false,
},
},
{
path: "role",
component: "system/role/index",
name: "Role",
meta: {
title: "仿真评估2",
icon: "role",
hidden: false,
roles: ["ADMIN6", "GUEST", "ADMIN"],
keepAlive: true,
alwaysShow: false,
},
},
],
},
{
path: "/operatorLibrary",
component: "Layout",
redirect: "/operatorLibrary/index",
name: "/operatorLibrary",
meta: {
title: "算子库管理",
icon: "dict",
hidden: false,
roles: ["GUEST", "ADMIN", "ADMIN6"],
alwaysShow: false,
},
children: [
{
path: "user",
component: "system/user/index",
name: "User",
meta: {
title: "算子库管理1",
icon: "user",
hidden: false,
roles: ["ADMIN", "GUEST"],
keepAlive: true,
alwaysShow: false,
},
},
{
path: "role",
component: "system/role/index",
name: "Role",
meta: {
title: "算子库管理2",
icon: "role",
hidden: false,
roles: ["ADMIN6", "GUEST", "ADMIN"],
keepAlive: true,
alwaysShow: false,
},
},
],
},
{
path: "/dataMgr",
component: "Layout",
redirect: "/dataMgr/user",
name: "/dataMgr",
meta: {
title: "数据管理",
icon: "edit",
hidden: false,
roles: ["GUEST", "ADMIN", "ADMIN6"],
alwaysShow: false,
},
children: [
{
path: "user",
component: "system/user/index",
name: "User",
meta: {
title: "数据管理1",
icon: "user",
hidden: false,
roles: ["ADMIN", "GUEST"],
keepAlive: true,
alwaysShow: false,
},
},
{
path: "role",
component: "system/role/index",
name: "Role",
meta: {
title: "数据管理2",
icon: "role",
hidden: false,
roles: ["ADMIN6", "GUEST", "ADMIN"],
keepAlive: true,
alwaysShow: false,
},
},
],
},
{
path: "/tester",
component: "Layout",
redirect: "/tester/index",
name: "/tester",
meta: {
title: "算法测试仪管理",
icon: "link",
hidden: false,
roles: ["GUEST", "ADMIN", "ADMIN6"],
alwaysShow: false,
},
children: [
{
path: "user",
component: "system/user/index",
name: "User",
meta: {
title: "算法测试仪管理1",
icon: "user",
hidden: false,
roles: ["ADMIN", "GUEST"],
keepAlive: true,
alwaysShow: false,
},
},
{
path: "role",
component: "system/role/index",
name: "Role",
meta: {
title: "算法算法测试仪管理2",
icon: "role",
hidden: false,
roles: ["ADMIN6", "GUEST", "ADMIN"],
keepAlive: true,
alwaysShow: false,
},
},
],
},
{
path: "/system",
component: "Layout",
redirect: "/system/index",
name: "/system",
meta: {
title: "系统管理",
icon: "menu",
hidden: false,
roles: ["GUEST", "ADMIN", "ADMIN6"],
alwaysShow: false,
},
children: [
{
path: "user",
component: "system/user/index",
name: "User",
meta: {
title: "系统管理1",
icon: "user",
hidden: false,
roles: ["ADMIN", "GUEST"],
keepAlive: true,
alwaysShow: false,
},
},
{
path: "role",
component: "system/role/index",
name: "Role",
meta: {
title: "系统管理2",
icon: "role",
hidden: false,
roles: ["ADMIN6", "GUEST", "ADMIN"],
keepAlive: true,
alwaysShow: false,
},
},
],
},
]);
});
/*
return request<any, RouteVO[]>({
url: "/api/v1/menus/routes",
method: "get",
});*/
}
/**
*
*
* @param queryParams
*/
static getList(queryParams: MenuQuery) {
return request<any, MenuVO[]>({
url: "/api/v1/menus",
method: "get",
params: queryParams,
});
}
/**
*
*/
static getOptions() {
return request<any, OptionType[]>({
url: "/api/v1/menus/options",
method: "get",
});
}
/**
*
*
* @param id
*/
static getFormData(id: number) {
return request<any, MenuForm>({
url: "/api/v1/menus/" + id + "/form",
method: "get",
});
}
/**
*
*
* @param data
*/
static add(data: MenuForm) {
return request({
url: "/api/v1/menus",
method: "post",
data: data,
});
}
/**
*
*
* @param id
* @param data
*/
static update(id: string, data: MenuForm) {
return request({
url: "/api/v1/menus/" + id,
method: "put",
data: data,
});
}
/**
*
*
* @param id ID
*/
static deleteById(id: number) {
return request({
url: "/api/v1/menus/" + id,
method: "delete",
});
}
}
export default MenuAPI;

View File

@ -0,0 +1,181 @@
import { MenuTypeEnum } from "@/enums/MenuTypeEnum";
/**
*
*/
export interface MenuQuery {
keywords?: string;
}
/**
*
*/
export interface MenuVO {
/**
*
*/
children?: MenuVO[];
/**
*
*/
component?: string;
/**
* ICON
*/
icon?: string;
/**
* ID
*/
id?: number;
/**
*
*/
name?: string;
/**
* ID
*/
parentId?: number;
/**
*
*/
perm?: string;
/**
*
*/
redirect?: string;
/**
*
*/
routeName?: string;
/**
*
*/
routePath?: string;
/**
* ()
*/
sort?: number;
/**
*
*/
type?: MenuTypeEnum;
/**
* (1:;0:)
*/
visible?: number;
}
/**
*
*/
export interface MenuForm {
/**
* ID
*/
id?: string;
/**
* ID
*/
parentId?: number;
/**
*
*/
name?: string;
/**
* (1:;0:;)
*/
visible: number;
icon?: string;
/**
*
*/
sort: number;
/**
*
*/
component?: string;
/**
*
*/
path?: string;
/**
*
*/
redirect?: string;
/**
*
*/
type: MenuTypeEnum;
/**
*
*/
perm?: string;
/**
*
*/
keepAlive?: number;
/**
*
*/
alwaysShow?: number;
}
/**
* RouteVO
*/
export interface RouteVO {
/**
*
*/
children: RouteVO[];
/**
*
*/
component?: string;
meta?: Meta;
/**
*
*/
name?: string;
/**
*
*/
path?: string;
/**
*
*/
redirect?: string;
}
/**
* Meta
*/
export interface Meta {
/**
*
*/
alwaysShow?: boolean;
/**
* (true- false-)
*/
hidden?: boolean;
/**
* ICON
*/
icon?: string;
/**
*
*/
keepAlive?: boolean;
/**
*
*/
roles?: string[];
/**
* title
*/
title?: string;
}

View File

@ -0,0 +1,108 @@
import request from "@/utils/request";
import { RoleQuery, RolePageResult, RoleForm } from "./model";
class RoleAPI {
/**
*
*
* @param queryParams
*/
static getPage(queryParams?: RoleQuery) {
return request<any, RolePageResult>({
url: "/api/v1/roles/page",
method: "get",
params: queryParams,
});
}
/**
*
*
* @param queryParams
*/
static getOptions(queryParams?: RoleQuery) {
return request<any, OptionType[]>({
url: "/api/v1/roles/options",
method: "get",
params: queryParams,
});
}
/**
* ID
*
* @param queryParams
*/
static getRoleMenuIds(roleId: number) {
return request<any, number[]>({
url: "/api/v1/roles/" + roleId + "/menuIds",
method: "get",
});
}
/**
*
*
* @param queryParams
*/
static updateRoleMenus(roleId: number, data: number[]) {
return request({
url: "/api/v1/roles/" + roleId + "/menus",
method: "put",
data: data,
});
}
/**
*
*
* @param id
*/
static getFormData(id: number) {
return request<any, RoleForm>({
url: "/api/v1/roles/" + id + "/form",
method: "get",
});
}
/**
*
*
* @param data
*/
static add(data: RoleForm) {
return request({
url: "/api/v1/roles",
method: "post",
data: data,
});
}
/**
*
*
* @param id
* @param data
*/
static update(id: number, data: RoleForm) {
return request({
url: "/api/v1/roles/" + id,
method: "put",
data: data,
});
}
/**
* (,)
*
* @param ids
*/
static deleteByIds(ids: string) {
return request({
url: "/api/v1/roles/" + ids,
method: "delete",
});
}
}
export default RoleAPI;

View File

@ -0,0 +1,78 @@
/**
*
*/
export interface RoleQuery extends PageQuery {
keywords?: string;
}
/**
*
*/
export interface RolePageVO {
/**
*
*/
code?: string;
/**
* ID
*/
id?: number;
/**
*
*/
name?: string;
/**
*
*/
sort?: number;
/**
*
*/
status?: number;
/**
*
*/
createTime?: Date;
/**
*
*/
updateTime?: Date;
}
/**
*
*/
export type RolePageResult = PageResult<RolePageVO[]>;
/**
*
*/
export interface RoleForm {
/**
* ID
*/
id?: number;
/**
*
*/
code: string;
/**
*
*/
dataScope?: number;
/**
*
*/
name: string;
/**
*
*/
sort?: number;
/**
* (1-0-)
*/
status?: number;
}

View File

@ -0,0 +1,154 @@
import request from "@/utils/request";
import { UserForm, UserInfo, UserPageVO, UserQuery } from "./model";
class UserAPI {
/**
*
*/
static getInfo() {
return new Promise((resolve)=>{
resolve({
"userId": 2,
"username": "admin",
"nickname": "系统管理员",
"avatar": "https://oss.youlai.tech/youlai-boot/2023/05/16/811270ef31f548af9cffc026dfc3777b.gif",
"roles": [
"ADMIN"
],
"perms": []
})
})
/*
return request<any, UserInfo>({
url: "/api/v1/users/me",
method: "get",
});*/
}
/**
*
*
* @param queryParams
*/
static getPage(queryParams: UserQuery) {
return request<any, PageResult<UserPageVO[]>>({
url: "/api/v1/users/page",
method: "get",
params: queryParams,
});
}
/**
*
*
* @param userId
*/
static getFormData(userId: number) {
return request<any, UserForm>({
url: "/api/v1/users/" + userId + "/form",
method: "get",
});
}
/**
*
*
* @param data
*/
static add(data: UserForm) {
return request({
url: "/api/v1/users",
method: "post",
data: data,
});
}
/**
*
*
* @param id
* @param data
*/
static update(id: number, data: UserForm) {
return request({
url: "/api/v1/users/" + id,
method: "put",
data: data,
});
}
/**
*
*
* @param id
* @param password
*/
static updatePassword(id: number, password: string) {
return request({
url: "/api/v1/users/" + id + "/password",
method: "patch",
params: { password: password },
});
}
/**
*
*
* @param ids
*/
static deleteByIds(ids: string) {
return request({
url: "/api/v1/users/" + ids,
method: "delete",
});
}
/**
*
*
* @returns
*/
static downloadTemplate() {
return request({
url: "/api/v1/users/template",
method: "get",
responseType: "arraybuffer",
});
}
/**
*
*
* @param queryParams
* @returns
*/
static export(queryParams: UserQuery) {
return request({
url: "/api/v1/users/export",
method: "get",
params: queryParams,
responseType: "arraybuffer",
});
}
/**
*
*
* @param file
*/
static import(deptId: number, file: File) {
const formData = new FormData();
formData.append("file", file);
return request({
url: "/api/v1/users/import",
method: "post",
params: { deptId: deptId },
data: formData,
headers: {
"Content-Type": "multipart/form-data",
},
});
}
}
export default UserAPI;

View File

@ -0,0 +1,115 @@
/**
*
*/
export interface UserInfo {
userId?: number;
username?: string;
nickname?: string;
avatar?: string;
roles: string[];
perms: string[];
}
/**
*
*/
export interface UserQuery extends PageQuery {
keywords?: string;
status?: number;
deptId?: number;
startTime?: string;
endTime?: string;
}
/**
*
*/
export interface UserPageVO {
/**
*
*/
avatar?: string;
/**
*
*/
createTime?: Date;
/**
*
*/
deptName?: string;
/**
*
*/
email?: string;
/**
*
*/
genderLabel?: string;
/**
* ID
*/
id?: number;
/**
*
*/
mobile?: string;
/**
*
*/
nickname?: string;
/**
* 使(,)
*/
roleNames?: string;
/**
* (1:;0:)
*/
status?: number;
/**
*
*/
username?: string;
}
/**
*
*/
export interface UserForm {
/**
*
*/
avatar?: string;
/**
* ID
*/
deptId?: number;
/**
*
*/
email?: string;
/**
*
*/
gender?: number;
/**
* ID
*/
id?: number;
mobile?: string;
/**
*
*/
nickname?: string;
/**
* ID
*/
roleIds?: number[];
/**
* (1:;0:)
*/
status?: number;
/**
*
*/
username?: string;
}

View File

@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M499.2 671.232v-261.12h102.4c16.384 0 28.672 1.024 37.888 2.56 13.312 2.048 24.576 6.656 34.816 13.312 9.728 6.656 17.92 16.384 23.552 28.16 6.144 12.288 8.704 25.6 8.192 38.4 0 23.552-7.68 44.032-23.04 59.904-15.36 16.896-40.96 25.088-78.848 25.088h-43.52v93.184l-61.44.512zm281.6 0h-61.952v-261.12H780.8v261.12zm-287.744 0h-69.12L396.8 601.6h-73.728l-25.088 69.632h-66.56l100.352-261.12h54.272l107.008 261.12zM343.552 545.28h32.256l-15.872-42.496c0-.512-.512-1.024-.512-1.536l-15.872 44.032zm217.6-26.112h43.52c20.48 0 28.16-4.608 31.232-7.168 4.608-4.096 7.168-10.752 7.168-18.944 0-6.656-1.536-11.776-4.096-15.36-2.56-3.584-6.144-6.144-10.752-7.68-1.536-.512-6.656-1.536-24.064-1.536h-43.008v50.688z"/><path d="M747.52 842.752H512c-8.704 0-16.384-3.584-22.016-9.728-6.144-6.144-9.216-14.336-8.704-22.528.512-16.896 14.336-30.72 31.232-31.232H747.52c115.712 0 209.408-94.208 209.408-209.408 0-104.96-78.848-194.56-183.296-207.872l-22.528-3.072-4.608-22.016C724.992 231.936 631.808 156.16 524.288 156.16c-124.928 0-226.304 101.376-226.304 226.304v8.704l1.536 36.352-36.352-4.096c-6.144-1.024-12.288-1.024-18.432-1.024-98.304 0-178.176 79.872-178.176 178.176 0 98.304 79.872 178.176 178.176 178.176h63.488c8.704 0 16.384 3.584 22.016 9.728 6.144 6.144 9.216 14.336 8.704 22.528-.512 16.896-14.336 30.72-31.232 31.232h-64c-64 0-123.904-25.088-169.472-70.144C28.16 726.528 3.072 665.6 3.072 601.088c0-129.536 103.936-236.544 232.448-241.152 12.288-157.184 149.504-276.48 307.2-266.24 59.904 3.584 118.784 27.136 165.888 65.536 45.568 37.376 77.824 87.04 94.208 143.872 125.952 26.112 217.088 137.728 217.088 266.752.512 151.04-121.856 272.896-272.384 272.896z"/><path d="M572.416 930.816c-8.192 0-15.872-3.072-21.504-8.704L431.616 812.544l113.152-117.76c6.144-6.144 13.824-9.216 22.528-9.216 8.704 0 16.384 3.072 22.528 9.216 11.776 11.776 12.288 31.232 1.024 44.032l-68.608 70.656 71.68 66.048c6.144 5.632 9.728 13.312 10.24 22.016.512 8.704-2.56 16.384-8.192 23.04-6.656 6.656-14.848 10.24-23.552 10.24z"/></svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M917.6 267.2c-36.1-2.5-72.4-9.3-103.6-19.3-10.1-3-20.2-6.4-30.3-10-21.4-6.3-50.5-18.8-83.6-36.6-.4-.2-.7-.4-1.1-.6-7.8-4.2-15.7-8.7-23.8-13.4-10.9-6.3-21.7-12.9-32.5-19.9-.4-.3-.8-.5-1.2-.8-7.7-5-15.5-10.2-23.1-15.5-5-3.4-10-7.1-15-10.7-3.8-2.8-7.5-5.3-11.3-8.2-27.4-20.5-54.5-43.5-79.9-68.3-25.4 24.8-52.5 47.8-79.9 68.3-3.7 2.8-7.5 5.4-11.3 8.2-5 3.6-10 7.3-15 10.7-7.7 5.4-15.4 10.5-23.1 15.5-.4.3-.8.5-1.2.8-10.8 6.9-21.6 13.6-32.5 19.9-8.1 4.7-16 9.2-23.8 13.4-.3.2-.7.4-1 .6-33 17.8-62.2 30.3-83.6 36.6-10.1 3.6-20.2 7-30.3 10-31.1 10-67.4 16.8-103.6 19.3h.1c1.1 16.2 2.1 37.7 3.4 60.9h.7c6.1 86.8 23.5 210.2 49.7 282.8 1.2 3.2 2.2 6.5 3.3 9.6.6 1.5 1.2 2.8 1.8 4.3 62.8 162.1 171.9 280.1 303 323.4v.4c17.3 5.7 31.9 9.3 43.5 11.5 11.5-2.2 26.1-5.8 43.5-11.5v-.4C687 905 796.1 787 858.9 624.8c.6-1.5 1.2-2.8 1.8-4.3 1.2-3.1 2.2-6.4 3.3-9.6 26.2-72.5 43.6-196 49.7-282.8h.7c1.1-23.3 2.2-44.7 3.2-60.9zm-47.4 41.9-.5 9.5c-.5 2.2-.9 4.4-1 6.6C863 406 847 525.7 821.3 596.7c-.7 1.9-1.4 3.9-2 5.8-.4 1.2-.8 2.5-1.4 4.1-.5 1.2-1 2.5-1.4 3.4C758.1 760.8 657.7 869.3 541 907.8c-1.9.6-3.7 1.4-5.5 2.2-7.9 2.5-15.7 4.6-23.2 6.3-7.5-1.7-15.2-3.8-23.1-6.3-1.8-.9-3.6-1.6-5.5-2.2-116.7-38.5-217.1-147-275.4-297.5-.5-1.2-.9-2.4-1.7-4.1-.4-1.2-.8-2.4-1.3-3.6-.7-2-1.3-3.9-1.9-5.6-25.8-71.2-41.7-191-47.4-271.7-.2-2.3-.5-4.5-1-6.6l-.5-9.3c-.1-1.5-.2-3-.2-4.5 24.6-3.8 48.4-9.3 70-16.2 10.1-3 20.4-6.4 31.4-10.4 25.2-7.6 56.5-21.2 90.5-39.6.6-.3 1.2-.6 1.7-.9 8.2-4.4 16.7-9.2 24.8-14 10.7-6.1 22-13 34.5-21.1.4-.2 1-.6 1.3-.8 8.2-5.3 16.4-10.8 24.1-16.2 4.5-3.1 9.1-6.4 13.7-9.7l2.4-1.8 4-2.9c2.6-1.9 5.2-3.7 7.5-5.5 17.9-13.4 35.3-27.5 52-42.1 16.7 14.7 34 28.7 51.8 42 2.6 1.9 5.1 3.8 7.7 5.6l4.3 3.1 1.5 1.1c4.8 3.5 9.6 6.9 14 9.9 8.1 5.7 16.3 11.2 23.7 16l2.1 1.3c12.4 8 23.7 14.9 34.1 20.8 8.6 5 17 9.8 25 14.1.4.2 1 .5 1.5.8 34.2 18.4 65.6 32.1 90.9 39.7 11 3.9 21.3 7.3 30.6 10.1 22.1 7.1 46.1 12.6 70.8 16.5.1 1.5.1 3 0 4.4z"/><path d="M710.6 411.2 476.1 651.6l-120-123c-8.3-8.5-21.8-8.5-30.1 0s-8.3 22.3 0 30.9L461.1 698c4.2 4.3 9.6 6.4 15.1 6.4 5.4 0 10.9-2.1 15-6.4l249.5-255.7c8.3-8.5 8.3-22.3 0-30.9-8.3-8.7-21.8-8.7-30.1-.2z"/></svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M832.128 768c33.195 0 60.501 25.173 63.573 57.813L896 832a64 64 0 0 1-63.872 64H533.205a63.787 63.787 0 0 1-63.872-64 64 64 0 0 1 63.872-64h298.923zM213.333 874.667c-23.722 0-42.666-19.072-42.666-42.624V362.667A42.667 42.667 0 0 1 213.333 320l4.992.299C239.66 322.73 256 340.779 256 362.624l-.043 128.043h128.299c21.248 0 39.595 16.469 42.112 37.674l.299 4.992-.299 4.992A42.368 42.368 0 0 1 384.256 576H256l.043 213.333h128.256c22.869 0 42.41 19.115 42.41 42.667l-.298 4.992a42.368 42.368 0 0 1-42.112 37.675zm618.795-405.334c33.195 0 60.501 25.174 63.573 57.814l.299 6.186a64 64 0 0 1-63.872 64H533.205a63.787 63.787 0 0 1-63.872-64 64 64 0 0 1 63.872-64h298.923zM576.171 128c33.194 0 60.458 25.173 63.573 57.813L640 192c0 35.328-29.013 64-63.83 64H191.83A63.744 63.744 0 0 1 128 192c0-35.328 29.013-64 63.83-64h384.34z"/></svg>

After

Width:  |  Height:  |  Size: 941 B

View File

@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M962.184 55.874H61.818C27.732 55.874 0 83.606 0 117.692v621.64c0 34.086 27.732 61.818 61.818 61.818h308.52v44.98c0 41.234-33.547 74.782-74.781 74.782h-67.995c-13.036 0-23.606 10.568-23.606 23.606 0 13.038 10.57 23.606 23.606 23.606h568.874c13.036 0 23.606-10.568 23.606-23.606 0-13.038-10.57-23.606-23.606-23.606h-67.997c-41.234 0-74.782-33.548-74.782-74.782v-44.978h308.52c34.087 0 61.821-27.732 61.821-61.819v-621.64c.004-34.087-27.728-61.819-61.814-61.819zM391.84 920.916c16.092-20.672 25.714-46.616 25.714-74.782v-44.98h188.894v44.98c0 28.166 9.622 54.112 25.714 74.782H391.841zm584.95-181.583c0 8.054-6.552 14.608-14.608 14.608H61.818c-8.054 0-14.608-6.552-14.608-14.608V615.267h929.58v124.066zm0-171.28H47.212v-450.36c0-8.055 6.552-14.609 14.608-14.609h900.362c8.054 0 14.61 6.552 14.61 14.608v450.361z"/><path d="M486.531 684.611a25.476 25.476 0 1 0 50.952 0 25.476 25.476 0 1 0-50.952 0zm65.946-466.103c-9.22-9.218-24.162-9.218-33.386 0L352.263 385.337c-9.218 9.218-9.218 24.166 0 33.386a23.534 23.534 0 0 0 16.694 6.914 23.526 23.526 0 0 0 16.692-6.914l166.828-166.829c9.218-9.218 9.218-24.166 0-33.386zm98.88 96.679c-9.216-9.218-24.158-9.218-33.384-.002l-66.46 66.456c-9.218 9.22-9.218 24.168 0 33.386a23.53 23.53 0 0 0 16.692 6.914c6.04 0 12.082-2.304 16.692-6.914l66.46-66.456c9.218-9.218 9.218-24.166 0-33.384z"/></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="1em" height="1em" viewBox="0 0 36 36"><path d="m19.41 18 8.29-8.29a1 1 0 0 0-1.41-1.41L18 16.59l-8.29-8.3a1 1 0 0 0-1.42 1.42l8.3 8.29-8.3 8.29A1 1 0 1 0 9.7 27.7l8.3-8.29 8.29 8.29a1 1 0 0 0 1.41-1.41z" fill="currentColor"/></svg>

After

Width:  |  Height:  |  Size: 297 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="1em" height="1em" viewBox="0 0 36 36"><path d="M26 17H10a1 1 0 0 0 0 2h16a1 1 0 0 0 0-2z" fill="currentColor"/></svg>

After

Width:  |  Height:  |  Size: 183 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="1em" height="1em" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="m7 12 7 7m-7-7 7-7" stroke-linejoin="round"/><path d="M21 12H7.5"/><path d="M3 3v18" stroke-linejoin="round"/></g></svg>

After

Width:  |  Height:  |  Size: 310 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="1em" height="1em" viewBox="0 0 20 20"><path d="M3 5h14V3H3v2zm12 8V7H5v6h10zM3 17h14v-2H3v2z" fill="currentColor"/></svg>

After

Width:  |  Height:  |  Size: 187 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="1em" height="1em" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="m17 12-7 7m7-7-7-7" stroke-linejoin="round"/><path d="M3 12h13.5"/><path d="M21 3v18" stroke-linejoin="round"/></g></svg>

After

Width:  |  Height:  |  Size: 311 B

View File

@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M449.6 116.2H303.8c-14.2 0-25.7-11.5-25.7-25.7s11.5-25.7 25.7-25.7h145.8c14.2 0 25.7 11.5 25.7 25.7s-11.5 25.7-25.7 25.7zm0 0"/><path d="M160.1 859.3c-14.2 0-25.7-11.5-25.7-25.7V167.4c0-56.6 46-102.6 102.6-102.6h66.8c14.2 0 25.7 11.5 25.7 25.7s-11.5 25.7-25.7 25.7H237c-28.2 0-51.1 22.9-51.1 51.1v666.2c-.1 14.3-11.6 25.8-25.8 25.8zm373.5-512.6c-6.3 0-12.4-1.3-17.6-3.5-13.5-5.8-21.9-17.9-21.9-31.6v-221c0-14.2 11.5-25.7 25.7-25.7s25.7 11.5 25.7 25.7v189l27.7-26.6c14.1-13.5 36.1-13.5 50.1 0l22.1 21.3V90.5c0-14.2 11.5-25.7 25.7-25.7s25.7 11.5 25.7 25.7v219.6c0 14.5-8.6 27.5-22 33.2-13.3 5.7-28.7 2.9-39.2-7.2l-37.5-36-37.5 36c-7.6 7.6-17.5 10.6-27 10.6zm0 0"/><path d="M846.1 958.9H236.9c-56.6 0-102.6-46-102.6-102.6v-22.8c0-14.2 11.5-25.7 25.7-25.7s25.7 11.5 25.7 25.7v22.8c0 28.2 22.9 51.1 51.1 51.1H846c14.2 0 25.7 11.5 25.7 25.7.1 14.3-11.4 25.8-25.6 25.8zm0 0"/><path d="M160.1 876h-.9c-14.2-.5-25.3-12.4-24.8-26.6 1-28.2 6.3-48.5 16.7-63.6 13.8-20.1 35.4-30.3 64.3-30.3h615c3.2-2.7 6.4-6.1 8.6-8.6V133.1c-1.8-5.1-11.7-15-16.8-16.8H449.6c-14.2 0-25.7-11.5-25.7-25.7s11.5-25.7 25.7-25.7h373.6c19.8 0 36.7 13.9 45 22.2 8.3 8.3 22.2 25.2 22.2 45v621.6c0 10.8-6.2 19.6-12.3 26.7-4.6 5.4-10.3 11-15.6 15.4-1 .9-2.1 1.7-3.2 2.5-5.4 4.1-12.9 8.8-22.3 8.8H215.3c-15 0-28 0-29.5 44.2-.5 13.8-11.9 24.7-25.7 24.7zm0 0"/><path d="M284.4 806.4c-14.2 0-25.7-11.5-25.7-25.7V90.5c0-14.2 11.5-25.7 25.7-25.7s25.7 11.5 25.7 25.7v690.1c0 14.3-11.5 25.8-25.7 25.8zM844.9 959h-1.6c-6.6-.3-30-2.3-52.2-16.9-19.5-12.7-42.6-38-42.6-86.3 0-62.3 35.7-101 93.1-101 14.2 0 25.7 11.5 25.7 25.7s-11.5 25.7-25.7 25.7c-12.5 0-41.7 0-41.7 49.6 0 21 6.6 35.3 20.1 43.8 10.6 6.6 22.1 7.8 25 8 1.4-.1 2.9 0 4.4.2 13.7 1.7 23.6 14 22.5 27.7-.9 9.5-8.8 23.5-27 23.5zm-1.8-51.3c-1.1.1-2.3.3-3.4.6 1.1-.3 2.2-.5 3.4-.6zm0 0"/></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M832.1 185.1H609.4l-17.1-62c-9.6-34.6-40.5-58.8-75.3-58.8H196c-43.2 0-78.3 36.4-78.3 81.1V897c0 35.3 28.7 64 64 64H832c35.3 0 64-28.7 64-64V249c.1-35.2-28.6-63.9-63.9-63.9zm-644.4-39.7c0-6.6 4.4-11.1 8.3-11.1h321c3.4 0 6.6 3.1 7.8 7.4l12 43.4H187.7v-39.7zm638.4 745.8H187.7V255.1h638.4v636.1z"/><path d="M311.1 415.1a35 35 0 1 0 70 0 35 35 0 1 0-70 0zm151.2-35h257.8v70H462.3zM311.1 582.3a35 35 0 1 0 70 0 35 35 0 1 0-70 0zm151.2-35h257.8v70H462.3zM311.1 749.5a35 35 0 1 0 70 0 35 35 0 1 0-70 0zm151.2-35h257.8v70H462.3z"/></svg>

After

Width:  |  Height:  |  Size: 640 B

View File

@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M624 706.3h-74.1V464c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v242.3H400c-6.7 0-10.4 7.7-6.3 12.9l112 141.7c3.2 4.1 9.4 4.1 12.6 0l112-141.7c4.1-5.2.4-12.9-6.3-12.9z"/><path d="M811.4 366.7C765.6 245.9 648.9 160 512.2 160S258.8 245.8 213 366.6C127.3 389.1 64 467.2 64 560c0 110.5 89.5 200 199.9 200H304c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8h-40.1c-33.7 0-65.4-13.4-89-37.7-23.5-24.2-36-56.8-34.9-90.6.9-26.4 9.9-51.2 26.2-72.1 16.7-21.3 40.1-36.8 66.1-43.7l37.9-9.9 13.9-36.6c8.6-22.8 20.6-44.1 35.7-63.4 14.9-19.2 32.6-35.9 52.4-49.9 41.1-28.9 89.5-44.2 140-44.2s98.9 15.3 140 44.2c19.9 14 37.5 30.8 52.4 49.9 15.1 19.3 27.1 40.7 35.7 63.4l13.8 36.5 37.8 10C846.1 454.5 884 503.8 884 560c0 33.1-12.9 64.3-36.3 87.7-23.4 23.4-54.5 36.3-87.6 36.3H720c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h40.1C870.5 760 960 670.5 960 560c0-92.7-63.1-170.7-148.6-193.3z"/></svg>

After

Width:  |  Height:  |  Size: 962 B

View File

@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M106.133 67.2a4.797 4.797 0 0 0-4.8 4.8c0 .187.014.36.027.533h-.027V118.4H9.6V26.667h50.133c2.654 0 4.8-2.147 4.8-4.8 0-2.654-2.146-4.8-4.8-4.8H9.6a9.594 9.594 0 0 0-9.6 9.6V118.4c0 5.307 4.293 9.6 9.6 9.6h91.733c5.307 0 9.6-4.293 9.6-9.6V72.533h-.026c.013-.173.026-.346.026-.533 0-2.653-2.146-4.8-4.8-4.8z"/><path d="M125.16 13.373 114.587 2.8c-3.747-3.747-9.854-3.72-13.6.027l-52.96 52.96a4.264 4.264 0 0 0-.907 1.36L33.813 88.533c-.746 1.76-.226 3.534.907 4.68 1.133 1.147 2.92 1.667 4.693.92l31.4-13.293c.507-.213.96-.52 1.36-.907l52.96-52.96c3.747-3.746 3.774-9.853.027-13.6zM66.107 72.4l-18.32 7.76 7.76-18.32L92.72 24.667l10.56 10.56L66.107 72.4zm52.226-52.227-8.266 8.267-10.56-10.56 8.266-8.267.027-.026 10.56 10.56-.027.026z"/></svg>

After

Width:  |  Height:  |  Size: 817 B

View File

@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="128" height="128"><path d="M512 128q69.675 0 135.51 21.163t115.498 54.997 93.483 74.837 73.685 82.006 51.67 74.837 32.17 54.827L1024 512q-2.347 4.992-6.315 13.483T998.87 560.17t-31.658 51.669-44.331 59.99-56.832 64.34-69.504 60.16-82.347 51.5-94.848 34.687T512 896q-69.675 0-135.51-21.163t-115.498-54.826-93.483-74.326-73.685-81.493-51.67-74.496-32.17-54.997L0 513.707q2.347-4.992 6.315-13.483t18.816-34.816 31.658-51.84 44.331-60.33 56.832-64.683 69.504-60.331 82.347-51.84 94.848-34.816T512 128.085zm0 85.333q-46.677 0-91.648 12.331t-81.152 31.83-70.656 47.146-59.648 54.485-48.853 57.686-37.675 52.821-26.325 43.99q12.33 21.674 26.325 43.52t37.675 52.351 48.853 57.003 59.648 53.845T339.2 767.02t81.152 31.488T512 810.667t91.648-12.331 81.152-31.659 70.656-46.848 59.648-54.186 48.853-57.344 37.675-52.651T927.957 512q-12.33-21.675-26.325-43.648t-37.675-52.65-48.853-57.345-59.648-54.186-70.656-46.848-81.152-31.659T512 213.334zm0 128q70.656 0 120.661 50.006T682.667 512 632.66 632.661 512 682.667 391.339 632.66 341.333 512t50.006-120.661T512 341.333zm0 85.334q-35.328 0-60.33 25.002T426.666 512t25.002 60.33T512 597.334t60.33-25.002T597.334 512t-25.002-60.33T512 426.666z"/></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1 @@
<svg width="128" height="64" xmlns="http://www.w3.org/2000/svg"><path d="M127.072 7.994c1.37-2.208.914-5.152-.914-6.87-2.056-1.717-4.797-1.226-6.396.982-.229.245-25.586 32.382-55.74 32.382-29.24 0-55.74-32.382-55.968-32.627-1.6-1.963-4.57-2.208-6.397-.49C-.17 3.086-.399 6.275 1.2 8.238c.457.736 5.94 7.36 14.62 14.72L4.17 35.96c-1.828 1.963-1.6 5.152.228 6.87.457.98 1.6 1.471 2.742 1.471s2.284-.49 3.198-1.472l12.564-13.983c5.94 4.416 13.021 8.587 20.788 11.53l-4.797 17.418c-.685 2.699.686 5.397 3.198 6.133h1.37c2.057 0 3.884-1.472 4.341-3.68L52.6 42.83c3.655.736 7.538 1.227 11.422 1.227 3.883 0 7.767-.49 11.422-1.227l4.797 17.173c.457 2.208 2.513 3.68 4.34 3.68.457 0 .914 0 1.143-.246 2.513-.736 3.883-3.434 3.198-6.133l-4.797-17.172c7.767-2.944 14.848-7.114 20.788-11.53l12.336 13.738c.913.981 2.056 1.472 3.198 1.472s2.284-.49 3.198-1.472c1.828-1.963 1.828-4.906.228-6.87l-11.65-13.001c9.366-7.36 14.849-14.474 14.849-14.474z"/></svg>

After

Width:  |  Height:  |  Size: 944 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"/></svg>

After

Width:  |  Height:  |  Size: 175 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M8 3v2H4v4H2V3h6zM2 21v-6h2v4h4v2H2zm20 0h-6v-2h4v-4h2v6zm0-12h-2V5h-4V3h6v6z"/></svg>

After

Width:  |  Height:  |  Size: 175 B

View File

@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M511.543 14.057C228.914 13.943 0 242.743 0 525.143 0 748.457 143.2 938.286 342.629 1008c26.857 6.743 22.742-12.343 22.742-25.371v-88.572C210.286 912.23 204 809.6 193.6 792.457c-21.029-35.886-70.743-45.028-55.886-62.171 35.315-18.172 71.315 4.571 113.029 66.171 30.171 44.686 89.028 37.143 118.857 29.714 6.514-26.857 20.457-50.857 39.657-69.485C248.571 727.886 181.6 629.829 181.6 513.257c0-56.571 18.629-108.571 55.2-150.514-23.314-69.143 2.171-128.343 5.6-137.143 66.4-5.943 135.429 47.543 140.8 51.771C420.914 267.2 464 261.83 512.229 261.83c48.457 0 91.657 5.6 129.714 15.885 12.914-9.828 76.914-55.771 138.628-50.171 3.315 8.8 28.229 66.628 6.286 134.857 37.029 42.057 55.886 94.514 55.886 151.2 0 116.8-67.429 214.971-228.572 243.314a145.714 145.714 0 0 1 43.543 104v128.572c.915 10.285 0 20.457 17.143 20.457 202.4-68.229 348.114-259.429 348.114-484.686 0-282.514-229.028-511.2-511.428-511.2z"/></svg>

After

Width:  |  Height:  |  Size: 1019 B

View File

@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M958.401 451.55a20.01 20.01 0 0 0-6.966-14.972L524.345 69.511c-7.499-6.446-18.581-6.446-26.08 0L309.583 231.676V129.657c0-11.05-8.902-19.533-19.952-19.533h-88.034c-11.048 0-19.928 8.482-19.928 19.533v211.954L71.176 436.578a20.003 20.003 0 0 0-6.968 15.174v105.5a20.007 20.007 0 0 0 33.052 15.172l53.298-45.826V850.7c0 60.678 49.364 110.042 110.042 110.042h504.192c60.678 0 110.043-49.364 110.043-110.042V527.026l51.586 44.336a20.001 20.001 0 0 0 21.48 2.966 20.006 20.006 0 0 0 11.566-18.343l-1.066-104.436zM221.579 150.033h48.095v115.942l-48.095 41.336V150.034zm349.14 770.692H436.665V700.642c0-11.03 8.977-20.007 20.008-20.007h94.036c11.03 0 20.007 8.976 20.007 20.007v220.084zm264.1-424.83v354.803c0 38.612-31.415 70.027-70.028 70.027H610.733V700.642c0-33.096-26.927-60.023-60.023-60.023h-94.036c-33.097 0-60.023 26.927-60.023 60.023v220.085H260.599c-38.612 0-70.027-31.415-70.027-70.027V495.895a20.07 20.07 0 0 0-.315-3.432L512.37 215.504l322.703 277.349a20.158 20.158 0 0 0-.255 3.042zM525.41 173.947c-7.502-6.446-18.587-6.447-26.086.003l-395.1 339.714v-52.727l407.081-349.87 407.177 349.952.522 51.205L525.41 173.948z"/></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M3 4h18v2H3V4zm0 15h18v2H3v-2zm8-5h10v2H11v-2zm0-5h10v2H11V9zm-8 3.5L7 9v7l-4-3.5z"/></svg>

After

Width:  |  Height:  |  Size: 180 B

View File

@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M0 512a512 512 0 1 0 1024 0A512 512 0 1 0 0 512z" fill="#FD8E66"/><path d="M377.745 354.306h63.05l-78.638 315.388h-63.05l78.638-315.388zm140.642 0h103.519c69.926 0 117.527 24.3 98.942 98.896-17.958 72.017-80.148 104.401-147.89 104.401H530.77L502.8 669.694h-63.05l78.637-315.388zm62.702 153.443c43.489 0 68.927-18.329 77.964-54.547 9.153-36.659-10.779-49.018-54.222-49.018h-35.823l-25.833 103.565h37.914z" fill="#FFF"/></svg>

After

Width:  |  Height:  |  Size: 535 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="m18.5 10 4.4 11h-2.155l-1.201-3h-4.09l-1.199 3h-2.154L16.5 10h2zM10 2v2h6v2h-1.968a18.221 18.221 0 0 1-3.62 6.301 14.865 14.865 0 0 0 2.335 1.707l-.75 1.878A17.016 17.016 0 0 1 9 13.725a16.677 16.677 0 0 1-6.201 3.548l-.536-1.929a14.7 14.7 0 0 0 5.327-3.042A18.078 18.078 0 0 1 4.767 8h2.24A16.031 16.031 0 0 0 9 10.877a16.165 16.165 0 0 0 2.91-4.876L2 6V4h6V2h2zm7.5 10.885L16.253 16h2.492L17.5 12.885z"/></svg>

After

Width:  |  Height:  |  Size: 501 B

View File

@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M611.2 368 316.8 662.4c-6.4 6.4-9.6 16-9.6 22.4s3.2 16 9.6 22.4 16 9.6 22.4 9.6 16-3.2 22.4-9.6L656 412.8c6.4-6.4 9.6-16 9.6-22.4s-3.2-16-9.6-22.4c-12.8-12.8-32-12.8-44.8 0z"/><path d="m608 755.2-99.2 99.2c-96 96-249.6 96-342.4 3.2-92.8-92.8-92.8-246.4 3.2-342.4l99.2-99.2c12.8-12.8 12.8-32 0-44.8s-32-12.8-44.8 0l-99.2 99.2C3.2 592 3.2 784 121.6 902.4 179.2 960 259.2 992 336 992c80 0 156.8-28.8 217.6-89.6l99.2-99.2c12.8-12.8 12.8-32 0-44.8s-32-16-44.8-3.2zm294.4-633.6C844.8 64 771.2 35.2 688 35.2h-3.2c-83.2 0-160 35.2-217.6 92.8l-96 96c-12.8 12.8-12.8 32 0 44.8s32 12.8 44.8 0l96-96c48-48 108.8-73.6 172.8-73.6h3.2c64 0 121.6 25.6 166.4 70.4 92.8 92.8 92.8 246.4-3.2 345.6l-96 96c-12.8 12.8-12.8 32 0 44.8 6.4 6.4 16 9.6 22.4 9.6s16-3.2 22.4-9.6l96-96c121.6-124.8 124.8-320 6.4-438.4z"/></svg>

After

Width:  |  Height:  |  Size: 909 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 1024 1024"><path fill="currentColor" d="M224 448a32 32 0 0 0-32 32v384a32 32 0 0 0 32 32h576a32 32 0 0 0 32-32V480a32 32 0 0 0-32-32zm0-64h576a96 96 0 0 1 96 96v384a96 96 0 0 1-96 96H224a96 96 0 0 1-96-96V480a96 96 0 0 1 96-96"/><path fill="currentColor" d="M512 544a32 32 0 0 1 32 32v192a32 32 0 1 1-64 0V576a32 32 0 0 1 32-32m192-160v-64a192 192 0 1 0-384 0v64zM512 64a256 256 0 0 1 256 256v128H256V320A256 256 0 0 1 512 64"/></svg>

After

Width:  |  Height:  |  Size: 512 B

View File

@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M374.272 440.832H127.488c-33.792 0-61.44-27.648-61.44-61.44V132.608c0-33.792 27.648-61.44 61.44-61.44h247.296c33.792 0 61.44 27.648 61.44 61.44v247.296c-.512 33.792-27.648 60.928-61.952 60.928zM127.488 132.608v247.296h247.296V132.608H127.488zM762.88 492.032c-16.384 0-31.744-6.144-43.52-17.92L544.768 299.52c-11.776-11.776-17.92-27.136-17.92-43.52s6.144-31.744 17.92-43.52L719.36 37.888c11.776-11.776 27.136-17.92 43.52-17.92s31.744 6.144 43.52 17.92L980.992 212.48c11.776 11.776 17.92 27.136 17.92 43.52s-6.144 31.744-17.92 43.52L806.4 474.112c-11.776 11.776-27.136 17.92-43.52 17.92zm0-410.624L588.288 256 762.88 430.592 937.472 256 762.88 81.408zM374.272 952.832H127.488c-33.792 0-61.44-27.648-61.44-61.44V644.096c0-33.792 27.648-61.44 61.44-61.44h247.296c33.792 0 61.44 27.648 61.44 61.44v247.296c-.512 34.304-27.648 61.44-61.952 61.44zM127.488 644.608v247.296h247.296V644.608H127.488zm758.784 308.224H638.976c-33.792 0-61.44-27.648-61.44-61.44V644.096c0-33.792 27.648-61.44 61.44-61.44h247.296c33.792 0 61.44 27.648 61.44 61.44v247.296c0 34.304-27.136 61.44-61.44 61.44zM639.488 644.608v247.296h247.296V644.608H639.488z"/></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M0 512a512 512 0 1 0 1024 0A512 512 0 1 0 0 512z" fill="#50DCB6"/><path d="M790.148 502.822c0-30.856-25.013-55.87-55.869-55.87H483.666c-30.855 0-55.869 25.014-55.869 55.87v158.432c0 30.856 25.014 55.87 55.87 55.87h229.557a47.894 47.894 0 0 1 26.28 7.854l35.81 23.508c6.37 4.18 14.834-.388 14.834-8.007V502.822z" fill="#FFF" fill-opacity=".4"/><path d="M233.852 320.848c0-30.856 25.013-55.869 55.869-55.869h366.341c30.856 0 55.87 25.013 55.87 55.869v245.026c0 30.855-25.014 55.869-55.87 55.869h-325.28a47.894 47.894 0 0 0-26.295 7.865l-55.799 36.66c-6.369 4.185-14.836-.384-14.836-8.004V320.848z" fill="#FFF"/><path d="M323.242 446.952a34.32 34.32 0 1 0 68.64 0 34.32 34.32 0 1 0-68.64 0zm115.729 0a34.32 34.32 0 1 0 68.64 0 34.32 34.32 0 1 0-68.64 0zm115.729 0a34.32 34.32 0 1 0 68.639 0 34.32 34.32 0 1 0-68.64 0z" fill="#46D7B0"/></svg>

After

Width:  |  Height:  |  Size: 949 B

View File

@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M0 512a512 512 0 1 0 1024 0A512 512 0 1 0 0 512z" fill="#FF822B"/><path d="M324.409 655.019c180.881 0 327.51-146.631 327.51-327.51 0-152.138-103.734-280.047-244.33-316.854C205.813 52.464 47.496 213.018 8.986 415.982c38.6 137.898 165.196 239.037 315.422 239.037z" fill="#FFF" fill-opacity=".2"/><path d="M512 1024c282.767 0 512-229.233 512-512 0-31.766-2.891-62.854-8.434-93.019-87.509-82.881-205.691-133.718-335.742-133.718-269.71 0-488.357 218.645-488.357 488.357 0 54.96 9.084 107.803 25.823 157.104C300.627 989.489 402.283 1024 512 1024z" fill="#FFF" fill-opacity=".15"/><path d="M732.536 756.566c36.39 0 65.89-29.5 65.89-65.89 0 36.39 29.502 65.89 65.889 65.89-17.054 0-65.89 29.503-65.89 65.89 0-36.387-29.5-65.89-65.889-65.89zM159.686 247.28c25.686 0 46.51-20.823 46.51-46.51 0 25.687 20.823 46.51 46.51 46.51-12.037 0-46.51 20.824-46.51 46.51 0-25.686-20.824-46.51-46.51-46.51z" fill="#FFF" fill-opacity=".5"/><path d="M206.195 333.323c8.563 0 15.504-6.94 15.504-15.503 0 8.562 6.94 15.503 15.503 15.503-4.012 0-15.503 6.941-15.503 15.504 0-8.563-6.941-15.504-15.504-15.504z" fill="#FFF" fill-opacity=".3"/><path d="M802.301 726.987c0 8.11-1.387 15.686-4.155 22.728-2.775 7.043-6.713 13.232-11.829 18.566-5.116 5.336-11.085 9.494-17.905 12.486-6.821 2.984-14.281 4.48-22.38 4.48H281.805c-8.1 0-15.773-1.496-23.019-4.48-7.247-2.992-13.641-7.15-19.183-12.486-5.542-5.334-9.912-11.523-13.108-18.566-3.198-7.042-4.796-14.618-4.796-22.728v-319.47c0-16.218 5.648-29.983 16.945-41.294 11.296-11.31 25.044-16.965 41.243-16.965h464.226c16.199 0 29.947 5.655 41.243 16.965 11.294 11.311 16.945 25.076 16.945 41.295v87.07h-145.15c-16.2 0-29.947 5.548-41.243 16.645-11.297 11.098-16.946 24.755-16.946 40.974.427 11.098 2.772 20.914 7.034 29.45 3.41 7.256 9.059 13.872 16.945 19.847 7.886 5.976 19.29 8.964 34.21 8.964H802.3v116.52zm-86.962-407.18H425.038c23.019-11.95 44.76-23.474 65.222-34.571a6020.558 6020.558 0 0 0 53.072-28.17c17.478-9.39 31.119-16.646 40.924-21.768 14.92-8.109 28.241-11.844 39.964-11.203 11.723.64 21.634 2.667 29.734 6.082 9.378 4.694 17.478 10.883 24.298 18.566l37.087 71.064zm-86.963 232.4c0-8.109 2.77-14.938 8.313-20.487 5.542-5.548 12.362-8.323 20.462-8.323s14.92 2.775 20.461 8.323c5.543 5.549 8.313 12.378 8.313 20.487 0 8.11-2.77 15.046-8.313 20.807-5.542 5.762-12.362 8.644-20.461 8.644-8.1 0-14.92-2.882-20.462-8.644-5.542-5.761-8.313-12.697-8.313-20.807z" fill="#FFF"/></svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><defs><style>@font-face{font-family:rbicon;src:url(chrome-extension://dipiagiiohfljcicegpgffpbnjmgjcnf/fonts/rbicon.woff2) format(&quot;woff2&quot;);font-weight:400;font-style:normal}</style></defs><path d="M64 64v576h832V64H64zM0 0h960v704H0V0z"/><path d="M192 896h576v64H192zm256-256h64v256h-64zm31.232-78.396 309.99-348.33-47.803-42.548-259.567 291.67-177.895-222.387L163.21 438.605l52.224 37.009 91.622-129.28z"/></svg>

After

Width:  |  Height:  |  Size: 525 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M10 7a7 7 0 0 0 12 4.9v.1c0 5.523-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2h.1A6.98 6.98 0 0 0 10 7zm-6 5a8 8 0 0 0 15.062 3.762A9 9 0 0 1 8.238 4.938 7.999 7.999 0 0 0 4 12z"/></svg>

After

Width:  |  Height:  |  Size: 272 B

View File

@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M0 512a512 512 0 1 0 1024 0A512 512 0 1 0 0 512z" fill="#F15BB5"/><path d="m772 432.8-7.9-63.3v-.2c-.1-1-.4-2.1-.7-3.1v-.3l-.2-.4-25.7-72.1c-.3-.7-.6-1.5-1-2.2-7-14.4-21.5-23.7-37.5-24h-24.1c-10 0-18.6 7.8-18.9 17.8-.4 10.5 8 19.1 18.4 19.1H699c1.9.3 3.4 1.4 4.2 3.1l13.5 37.9c.7 1.9.4 3.9-.7 5.6-1.1 1.6-3 2.6-5 2.6h-67.3c-3.5 0-6.2-2.8-6.2-6.2v-24.5c0-67.9-55.1-123-123-123s-123 55.1-123 123v24.5c0 3.5-2.8 6.2-6.2 6.2h-69.8c-3.4 0-6.2-2.8-6.2-6.2 0-.8.2-1.6.5-2.3l15.5-36.8c.7-2.1 2.5-3.5 4.7-3.9h24.1c10 0 18.6-7.8 18.9-17.8.4-10.5-8-19.1-18.4-19.1H330c-16.5.5-31.3 10.2-38.1 25.3l-30.6 72.1v.2c-.2.4-.3.8-.5 1.2v.4c-.3 1-.6 2.1-.7 3.1l-39 310.8c-6.4 50.5 29.5 96.7 80.1 103 3.8.5 7.7.7 11.5.7h94.5C514.8 718.5 680.7 597.9 772 432.8zM440.7 322.5c0-41 33.3-74.1 74.3-73.8 40.7.3 73.3 34.1 73.3 74.8V347c0 3.5-2.8 6.2-6.2 6.2H446.9c-3.5 0-6.2-2.8-6.2-6.2v-24.5zm152.7 257L514 662.4c-2.3 2.4-6.3 2.5-8.7.2l-.2-.2-79.4-82.9c-15.1-15.1-18.8-38.2-9.3-57.3 13.4-26.8 47.7-36.1 73.3-18.2 2.3 1.6 4.4 3.5 6.4 5.5l9.2 9.2c2.4 2.4 6.3 2.4 8.7 0l9.4-9.4c19.4-19.4 50.8-19.4 70.2 0 19.3 19.4 19.3 50.8-.2 70.2z" fill="#FFFDF3"/><path d="M803.7 691.6c0-3.8-.3-7.7-.7-11.4l-31-247.4c-91.3 165.1-257.2 285.7-364.8 351h304.1c51 0 92.4-41.2 92.4-92.2z" fill="#FFF" opacity=".9"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M520.235 504.577c51.305-33.512 85.254-91.477 85.254-157.301 0-103.485-84.273-187.757-187.758-187.757S229.974 243.79 229.974 347.275c0 65.715 33.95 123.68 85.255 157.301-47.266 15.72-90.603 42.245-126.845 78.486-61.24 61.24-94.97 142.675-94.97 229.239 0 13.318 10.698 24.016 24.015 24.016s24.015-10.699 24.015-24.016c0-152.28 123.899-276.177 276.178-276.177S693.801 660.02 693.801 812.302c0 13.318 10.697 24.016 24.014 24.016s24.016-10.699 24.016-24.016c0-86.564-33.73-168-94.97-229.237-36.024-36.134-79.36-62.768-126.627-78.488zm-242.23-157.192c0-77.067 62.658-139.726 139.726-139.726s139.727 62.658 139.727 139.726-62.66 139.726-139.727 139.726c-76.958 0-139.726-62.659-139.726-139.726z"/><path d="M871.406 599.002a323.731 323.731 0 0 0-150.643-119.86c27.072-40.28 41.481-87.765 41.045-136.778-.437-59.602-22.706-116.694-62.55-160.795-8.95-9.824-24.124-10.589-33.95-1.637-9.823 8.951-10.587 24.125-1.637 33.949 66.7 73.575 67.135 185.138.983 259.477-1.528 1.746-2.837 3.71-3.712 5.675-1.2 1.856-2.074 3.93-2.729 6.222-3.492 12.773 4.15 25.981 16.92 29.474C807.11 550.097 892.366 674.323 878.065 809.9c-1.419 13.208 8.188 24.998 21.396 26.417.873.109 1.746.109 2.51.109 12.117 0 22.597-9.17 23.907-21.504 7.968-75.868-11.353-152.608-54.471-215.921z"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M0 512a512 512 0 1 0 1024 0A512 512 0 1 0 0 512z" fill="#69ADF4"/><path d="m562.058 278.412 42.198-12.096q35.292-10.117 45.409 25.176l117.221 408.934q10.117 35.292-25.176 45.409l-42.197 12.096q-35.293 10.117-45.41-25.176l-117.22-408.934q-10.118-35.292 25.175-45.41z" fill="#FFF" opacity=".4"/><path d="M284.134 263.383c-20.276 0-36.714 16.437-36.714 36.714v43.897a7.981 7.981 0 0 0 7.981 7.981h55.47c13.444 0 24.343 10.899 24.343 24.343s-10.899 24.343-24.343 24.343h-55.47a7.981 7.981 0 0 0-7.98 7.981V725.5c0 20.276 16.437 36.714 36.713 36.714h43.897c20.276 0 36.714-16.438 36.714-36.714V300.097c0-20.277-16.438-36.714-36.714-36.714h-43.897zm152.443 0c-20.276 0-36.714 16.437-36.714 36.714v122.912a7.981 7.981 0 0 0 7.981 7.98h49.085c13.444 0 24.343 10.9 24.343 24.344s-10.9 24.343-24.343 24.343h-49.085a7.981 7.981 0 0 0-7.981 7.981V725.5c0 20.276 16.438 36.714 36.714 36.714h43.897c20.276 0 36.714-16.438 36.714-36.714V300.097c0-20.277-16.438-36.714-36.714-36.714h-43.897z" fill="#FFF"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M924.146 233.09v-3.216l-.643-.642v-2.572l-.642-.642v-.643l-.643-.643h-.643l-.642-.642v-.643l-1.93-1.929v-.642l-.642-.643v-1.286h-1.929l-.643-.643h-1.286l-.642-.643v-.642h-.643v-.643h-.643l-.642-.643h-.643v-.642h-2.572v-.643h-5.144l-.642.643h-.643v.642h-1.286l-.643.643h-1.286L112.708 516c-10.287 3.858-15.431 14.789-11.574 25.719 2.572 5.787 7.073 9.644 12.217 11.573l235.972 94.518 24.433 135.668c1.93 10.931 12.217 17.36 22.505 16.074 4.5-.643 8.358-3.215 10.931-6.43l87.445-87.444 178.104 71.37c10.287 3.858 21.218-.642 25.719-10.287l223.756-523.383.642-.643v-7.073l1.288-2.571zM364.113 610.517 173.79 534.646l604.399-230.83-414.077 306.7zm41.15 127.952-12.86-74.586 62.369 25.076-49.509 49.51zm264.906-5.143L406.55 627.877 858.562 293.53 670.17 733.326z"/></svg>

After

Width:  |  Height:  |  Size: 877 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="1em" height="1em" viewBox="0 0 512 512"><path d="m400 148-21.12-24.57A191.43 191.43 0 0 0 240 64C134 64 48 150 48 256s86 192 192 192a192.09 192.09 0 0 0 181.07-128" fill="none" stroke="currentColor" stroke-linecap="square" stroke-miterlimit="10" stroke-width="32"/><path d="M464 68.45V220a4 4 0 0 1-4 4H308.45a4 4 0 0 1-2.83-6.83L457.17 65.62a4 4 0 0 1 6.83 2.83z" fill="currentColor"/></svg>

After

Width:  |  Height:  |  Size: 458 B

View File

@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="256" height="256"><path d="M79.238 961.896v-25.442c0-109.28 28.835-214.892 81.139-297.416 48.427-76.396 115.304-131.573 195.508-161.896A240.785 240.785 0 0 1 279.488 300.5c0-131.538 104.331-238.535 232.547-238.535S744.546 168.962 744.546 300.5a240.854 240.854 0 0 1-76.742 176.988c190.87 73.004 276.992 277.131 276.992 458.966v25.442H79.238zM694.908 300.5c0-103.43-82.039-187.615-182.873-187.615-100.835 0-182.873 84.184-182.873 187.615 0 103.465 82.038 187.65 182.873 187.65 100.834 0 182.873-84.185 182.873-187.65zm-79.166 213.508a226.454 226.454 0 0 1-103.707 25.096A225.935 225.935 0 0 1 407.912 513.8C212.888 564.927 136.804 752.854 129.5 910.977h765.035c-7.997-167.4-95.227-347.746-278.793-396.97zm-143.411 37.246h79.407l39.739-8.48-45.242 65.664 30.6 227.527-64.8 56.908-69.197-56.908 40.535-227.527-50.78-65.665 39.738 8.48z"/></svg>

After

Width:  |  Height:  |  Size: 925 B

View File

@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="m586.667 494.933-8.534 40.534L533.333 416l-19.2 2.133-29.866 57.6H448l-8.533 19.2-25.6 51.2L384 473.6l-17.067 6.4 38.4 93.867H422.4l38.4-78.934h36.267l27.733-51.2L571.733 569.6h19.2l14.934-74.667h51.2v-19.2h-66.134l-4.266 19.2zM512 283.733c-117.333 0-213.333 96-213.333 213.334S394.667 710.4 512 710.4s213.333-96 213.333-213.333c0-119.467-96-213.334-213.333-213.334zm0 401.067c-104.533 0-189.867-85.333-189.867-189.867S407.467 305.067 512 305.067 701.867 390.4 701.867 494.933 616.533 684.8 512 684.8zM893.867 224c-44.8-14.933-134.4-44.8-179.2-66.133C625.067 113.067 590.933 83.2 556.8 57.6 544 46.933 529.067 42.667 512 42.667s-32 4.266-44.8 12.8c-34.133 25.6-68.267 55.466-157.867 100.266-44.8 21.334-134.4 51.2-179.2 66.134-29.866 10.666-46.933 36.266-44.8 66.133 8.534 98.133 32 288 89.6 405.333 68.267 134.4 236.8 238.934 302.934 279.467 10.666 6.4 21.333 8.533 34.133 8.533s23.467-2.133 34.133-8.533c66.134-40.533 234.667-145.067 302.934-279.467 57.6-117.333 81.066-307.2 89.6-401.066 2.133-32-17.067-59.734-44.8-68.267zm-83.2 450.133C768 759.467 672 846.933 524.8 936.533c-4.267 0-8.533 2.134-12.8 2.134s-8.533-2.134-10.667-4.267C352 846.933 256 757.333 213.333 674.133 153.6 554.667 132.267 347.733 128 288c-2.133-14.933 12.8-21.333 17.067-23.467l17.066-6.4c49.067-17.066 125.867-42.666 168.534-64 81.066-40.533 119.466-70.4 151.466-93.866 4.267-4.267 8.534-6.4 12.8-10.667 0-2.133 6.4-4.267 14.934-4.267h4.266c8.534 0 14.934 2.134 17.067 4.267 4.267 4.267 8.533 6.4 12.8 10.667 29.867 23.466 68.267 53.333 151.467 93.866 42.666 21.334 117.333 46.934 168.533 64l17.067 6.4c4.266 2.134 17.066 6.4 17.066 23.467-6.4 59.733-27.733 266.667-87.466 386.133z"/></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="m12 1 9.5 5.5v11L12 23l-9.5-5.5v-11L12 1zm0 2.311L4.5 7.653v8.694l7.5 4.342 7.5-4.342V7.653L12 3.311zM12 16a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm0-2a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/></svg>

After

Width:  |  Height:  |  Size: 267 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M10 6v15H8V6H2V4h14v2h-6zm8 8v7h-2v-7h-3v-2h8v2h-3z"/></svg>

After

Width:  |  Height:  |  Size: 149 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M12 18a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm0-2a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM11 1h2v3h-2V1zm0 19h2v3h-2v-3zM3.515 4.929l1.414-1.414L7.05 5.636 5.636 7.05 3.515 4.93zM16.95 18.364l1.414-1.414 2.121 2.121-1.414 1.414-2.121-2.121zm2.121-14.85 1.414 1.415-2.121 2.121-1.414-1.414 2.121-2.121zM5.636 16.95l1.414 1.414-2.121 2.121-1.414-1.414 2.121-2.121zM23 11v2h-3v-2h3zM4 11v2H1v-2h3z"/></svg>

After

Width:  |  Height:  |  Size: 473 B

View File

@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M139 669.6V164.3c0-12.7 10.3-23.1 23.1-23.1h694.4c12.7 0 23.1 10.4 23.1 23.1v248.5h70V164.3c0-51.3-41.8-93.1-93.1-93.1H162c-51.3.1-93 41.8-93 93.1v505.3c0 51.3 41.8 93.1 93.1 93.1h224.7v-70H162c-12.7 0-23-10.4-23-23.1zm-34.3 131h282v70h-282z"/><path d="m954.9 599.4-5.1-15c-11.5-33.9-29.4-64.9-53.2-91.9l-10.5-11.9h-83.2l-41.7-72.2-15.6-3.1c-34.8-6.9-71.3-6.9-106.1 0l-15.6 3.1-41.7 72.2H499l-10.5 11.9c-23.8 27.1-41.7 58-53.2 91.9l-5.1 15 41.7 72.2-41.7 72.2 5.1 15c11.5 33.9 29.4 64.9 53.2 91.9l10.5 11.9h83.2l41.7 72.2 15.6 3.1c17.4 3.5 35.3 5.2 53.1 5.2s35.6-1.8 53.1-5.2l15.6-3.1 41.7-72.2h83.2l10.5-11.9c23.8-27.1 41.7-58 53.2-91.9l5.1-15-41.7-72.2 41.6-72.2zm-76.8 151.2c-6.4 14.9-14.5 29-24.3 42h-91.2l-45.6 79c-16.1 1.9-32.4 1.9-48.5 0l-45.6-79h-91.2c-9.8-13-17.9-27-24.3-42l45.6-79.1-45.6-79.1c6.4-14.9 14.5-29 24.3-42h91.2l45.6-79c16.1-1.9 32.4-1.9 48.5 0l45.6 79h91.2c9.8 13 17.9 27 24.3 42l-45.6 79.1 45.6 79.1z"/><path d="M692.7 560.2c-61.4 0-111.3 49.9-111.3 111.3s49.9 111.3 111.3 111.3S804 732.9 804 671.5c0-61.3-49.9-111.3-111.3-111.3zm0 152.7c-22.8 0-41.3-18.5-41.3-41.3s18.5-41.3 41.3-41.3 41.3 18.5 41.3 41.3-18.5 41.3-41.3 41.3z"/></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M0 64v896h1024V64H0zm384 576V448h256v192H384zm256 64v192H384V704h256zm0-512v192H384V192h256zm-320 0v192H64V192h256zM64 448h256v192H64V448zm640 0h256v192H704V448zm0-64V192h256v192H704zM64 704h256v192H64V704zm640 192V704h256v192H704z"/></svg>

After

Width:  |  Height:  |  Size: 351 B

View File

@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M0 512a512 512 0 1 0 1024 0A512 512 0 1 0 0 512z" fill="#FF8F41"/><path d="M466.107 217.091h93.382q29.53 0 29.53 29.531v51.08q0 29.531-29.53 29.531h-93.381q-29.531 0-29.531-29.53v-51.081q0-29.53 29.53-29.53zm253.476 125.01 40.039-17.701c3.508-1.55 4.021-6.316.924-8.579l-55.735-40.715c-3.096-2.263-7.481-.325-7.89 3.488l-4.477 41.544-58.874 80.592 28.157 20.568 57.856-79.197z" fill="#FFF" opacity=".4"/><path d="M263.383 521.178a249.016 249.016 0 1 0 498.032 0 249.016 249.016 0 1 0-498.032 0z" fill="#FFF"/><path d="M512.4 348.383c9.917 0 17.957 8.04 17.957 17.958v143.565l97.432 55.102c8.634 4.882 11.674 15.839 6.792 24.471-4.882 8.634-15.84 11.674-24.472 6.791l-106.55-60.258a17.958 17.958 0 0 1-9.118-15.632V366.341c0-9.917 8.04-17.958 17.958-17.958z" fill="#FF8F41"/><path d="M472.493 524.371a40.705 40.705 0 1 0 81.409 0 40.705 40.705 0 1 0-81.41 0z" fill="#FF8338"/></svg>

After

Width:  |  Height:  |  Size: 992 B

View File

@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M977.455 558.545h-34.91V453.818c0-44.218-37.236-81.454-81.454-81.454H546.909v-93.091h197.818c25.6 0 46.546-20.946 46.546-46.546V93.091c0-25.6-20.946-46.546-46.546-46.546H279.273c-25.6 0-46.546 20.946-46.546 46.546v139.636c0 25.6 20.946 46.546 46.546 46.546H477.09v93.09H162.909c-44.218 0-81.454 37.237-81.454 81.455v104.727h-34.91C20.945 558.545 0 579.491 0 605.091v325.818c0 25.6 20.945 46.546 46.545 46.546h139.637c25.6 0 46.545-20.946 46.545-46.546V605.091c0-25.6-20.945-46.546-46.545-46.546h-34.91V453.818c0-6.982 4.655-11.636 11.637-11.636h314.182v116.363h-34.91c-25.6 0-46.545 20.946-46.545 46.546v325.818c0 25.6 20.946 46.546 46.546 46.546h139.636c25.6 0 46.546-20.946 46.546-46.546V605.091c0-25.6-20.946-46.546-46.546-46.546H546.91V442.182h314.182c6.982 0 11.636 4.654 11.636 11.636v104.727h-34.909c-25.6 0-46.545 20.946-46.545 46.546v325.818c0 25.6 20.945 46.546 46.545 46.546h139.637c25.6 0 46.545-20.946 46.545-46.546V605.091c0-25.6-20.945-46.546-46.545-46.546zm-814.546 69.819v279.272H69.82V628.364h93.09zm395.636 0v279.272h-93.09V628.364h93.09zm-256-418.91v-93.09h418.91v93.09h-418.91zm651.637 698.182H861.09V628.364h93.09v279.272z"/></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M599.378 958.454H424.62c-165.805 0-296.769 0-296.769-86.024v-17.178c0-161.188 133.115-292.258 296.77-292.258h174.756c163.619 0 296.769 131.102 296.769 292.258v17.178c0 86.024-137.557 86.024-296.768 86.024zm-176.39-346.981c-137.625 0-249.608 109.935-249.608 245.098v17.491c0 35.046 144.255 35.046 249.608 35.046h177.985c87.207 0 249.645 0 249.645-35.046v-17.491c0-135.163-112.018-245.098-249.645-245.098H422.988zm80.266-83.526c-129.923 0-235.555-104.14-235.555-232.12 0-128.015 105.632-232.119 235.555-232.119s235.554 104.104 235.554 232.12c0 127.978-105.7 232.12-235.554 232.12zM316.246 295.098c0 101.224 83.91 183.572 187.008 183.572 103.133 0 187.042-82.348 187.042-183.572 0-101.19-83.909-183.502-187.042-183.502-103.134 0-187.008 82.311-187.008 183.502zm0 17.767"/></svg>

After

Width:  |  Height:  |  Size: 886 B

View File

@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M6.536 518.536a501.106 501.106 0 1 0 1002.213 0 501.106 501.106 0 1 0-1002.213 0z" fill="#FA6B6D"/><path d="M513.09 262.536c68.63 0 125.276 55.558 125.276 123.098 0 67.54-55.557 123.098-125.277 123.098-68.63 0-125.276-55.558-125.276-123.098 1.09-68.63 56.647-123.098 125.276-123.098zm0 0c68.63 0 125.276 55.558 125.276 123.098 0 67.54-55.557 123.098-125.277 123.098-68.63 0-125.276-55.558-125.276-123.098 1.09-68.63 56.647-123.098 125.276-123.098zm-46.843 286.502h104.579c89.327 0 161.225 70.809 161.225 159.047v9.804c0 34.86-71.898 35.95-161.225 35.95h-104.58c-89.327 0-161.225 0-161.225-35.95v-9.804c0-88.238 72.988-159.047 161.226-159.047z" fill="#FFF"/></svg>

After

Width:  |  Height:  |  Size: 774 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
src/assets/logo.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

@ -0,0 +1,33 @@
<template>
<component :is="type" v-bind="linkProps(to)">
<slot></slot>
</component>
</template>
<script setup lang="ts">
defineOptions({
name: "AppLink",
inheritAttrs: false,
});
import { isExternal } from "@/utils/index";
const props = defineProps({
to: {
type: String,
required: true,
},
});
const isExternalLink = computed(() => isExternal(props.to));
const type = computed(() => {
return isExternalLink.value ? "a" : "router-link";
});
const linkProps = (to: string) => {
return isExternalLink.value
? { href: to, target: "_blank", rel: "noopener noreferrer" }
: { to };
};
</script>

View File

@ -0,0 +1,97 @@
<template>
<el-breadcrumb class="flex-y-center">
<transition-group
enter-active-class="animate__animated animate__fadeInRight"
>
<el-breadcrumb-item v-for="(item, index) in breadcrumbs" :key="item.path">
<span
v-if="
item.redirect === 'noredirect' || index === breadcrumbs.length - 1
"
class="color-gray-400"
>{{ translateRouteTitle(item.meta.title) }}</span
>
<a v-else @click.prevent="handleLink(item)">
{{ translateRouteTitle(item.meta.title) }}
</a>
</el-breadcrumb-item>
</transition-group>
</el-breadcrumb>
</template>
<script setup lang="ts">
import { RouteLocationMatched } from "vue-router";
import { compile } from "path-to-regexp";
import router from "@/router";
import { translateRouteTitle } from "@/utils/i18n";
const currentRoute = useRoute();
const pathCompile = (path: string) => {
const { params } = currentRoute;
const toPath = compile(path);
return toPath(params);
};
const breadcrumbs = ref<Array<RouteLocationMatched>>([]);
function getBreadcrumb() {
let matched = currentRoute.matched.filter(
(item) => item.meta && item.meta.title
);
const first = matched[0];
if (!isDashboard(first)) {
matched = [
{ path: "/dashboard", meta: { title: "dashboard" } } as any,
].concat(matched);
}
breadcrumbs.value = matched.filter((item) => {
return item.meta && item.meta.title && item.meta.breadcrumb !== false;
});
}
function isDashboard(route: RouteLocationMatched) {
const name = route && route.name;
if (!name) {
return false;
}
return (
name.toString().trim().toLocaleLowerCase() ===
"Dashboard".toLocaleLowerCase()
);
}
function handleLink(item: any) {
const { redirect, path } = item;
if (redirect) {
router.push(redirect).catch((err) => {
console.warn(err);
});
return;
}
router.push(pathCompile(path)).catch((err) => {
console.warn(err);
});
}
watch(
() => currentRoute.path,
(path) => {
if (path.startsWith("/redirect/")) {
return;
}
getBreadcrumb();
}
);
onBeforeMount(() => {
getBreadcrumb();
});
</script>
<style lang="scss" scoped>
// element-plus
.el-breadcrumb__inner,
.el-breadcrumb__inner a {
font-weight: 400 !important;
}
</style>

View File

@ -0,0 +1,73 @@
<template>
<el-select
v-model="selectedValue"
:placeholder="placeholder"
:disabled="disabled"
clearable
@change="handleChange"
>
<el-option
v-for="option in options"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</el-select>
</template>
<script setup lang="ts">
import DictAPI from "@/api/dict";
const props = defineProps({
/**
* 字典类型编码(eg: 性别-gender)
*/
typeCode: {
type: String,
required: true,
},
modelValue: {
type: [String, Number],
},
placeholder: {
type: String,
default: "请选择",
},
disabled: {
type: Boolean,
default: false,
},
});
const emits = defineEmits(["update:modelValue"]); //
const options: Ref<OptionType[]> = ref([]); //
const selectedValue = ref<string | number | undefined>();
watch([options, () => props.modelValue], ([newOptions, newModelValue]) => {
if (newOptions.length === 0) return; //
if (newModelValue == undefined) {
selectedValue.value = undefined;
return;
}
if (typeof newOptions[0].value === "number") {
selectedValue.value = Number(newModelValue);
} else if (typeof newOptions[0].value === "string") {
selectedValue.value = String(newModelValue);
} else {
selectedValue.value = newModelValue;
}
});
function handleChange(val?: string | number | undefined) {
emits("update:modelValue", val);
}
onBeforeMount(() => {
// (typeCode)
DictAPI.getDictOptions(props.typeCode).then((data) => {
options.value = data;
});
});
</script>

View File

@ -0,0 +1,62 @@
<template>
<a
href="https://github.com/haoxianrui"
target="_blank"
class="github-corner"
aria-label="View source on Github"
>
<svg
width="80"
height="80"
viewBox="0 0 250 250"
style="color: #fff; fill: #40c9c6"
aria-hidden="true"
>
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z" />
<path
d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
fill="currentColor"
style="transform-origin: 130px 106px"
class="octo-arm"
/>
<path
d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
fill="currentColor"
class="octo-body"
/>
</svg>
</a>
</template>
<style scoped>
.github-corner:hover .octo-arm {
animation: octocat-wave 560ms ease-in-out;
}
@keyframes octocat-wave {
0%,
100% {
transform: rotate(0);
}
20%,
60% {
transform: rotate(-25deg);
}
40%,
80% {
transform: rotate(10deg);
}
}
@media (width <= 500px) {
.github-corner .octo-arm {
animation: octocat-wave 560ms ease-in-out;
}
.github-corner:hover .octo-arm {
animation: none;
}
}
</style>

View File

@ -0,0 +1,39 @@
<template>
<div
class="px-[15px] flex items-center justify-center color-[var(--el-text-color-regular)]"
@click="toggleClick"
>
<svg-icon
class="hamburger"
:class="{ 'is-active': isActive }"
icon-class="indent-decrease"
/>
</div>
</template>
<script setup lang="ts">
defineProps({
isActive: {
required: true,
type: Boolean,
default: false,
},
});
const emit = defineEmits(["toggleClick"]);
function toggleClick() {
emit("toggleClick");
}
</script>
<style scoped lang="scss">
.hamburger {
vertical-align: middle;
cursor: pointer;
transform: scaleX(-1);
}
.hamburger.is-active {
transform: scaleX(1);
}
</style>

View File

@ -0,0 +1,208 @@
<template>
<div ref="iconSelectRef" :style="'width:' + width">
<el-popover :visible="popoverVisible" :width="width" placement="bottom-end">
<template #reference>
<el-input
class="reference"
v-model="selectedIcon"
readonly
placeholder="点击选择图标"
@click="popoverVisible = !popoverVisible"
>
<template #prepend>
<template
v-if="selectedIcon && selectedIcon.startsWith('el-icon-')"
>
<el-icon>
<component :is="selectedIcon.replace('el-icon-', '')" />
</el-icon>
</template>
<template v-else>
<svg-icon :icon-class="selectedIcon" />
</template>
</template>
<template #suffix>
<el-icon
:style="{
transform: popoverVisible ? 'rotate(180deg)' : 'rotate(0)',
transition: 'transform .5s',
}"
@click="popoverVisible = !popoverVisible"
>
<ArrowDown />
</el-icon>
</template>
</el-input>
</template>
<!-- 下拉选择弹窗 -->
<div ref="popoverContentRef">
<el-input
v-model="searchText"
placeholder="搜索图标"
clearable
@input="filterIcons"
/>
<el-tabs v-model="activeTab" @tab-click="handleTabClick">
<el-tab-pane label="SVG 图标" name="svg">
<el-scrollbar height="300px">
<ul class="icon-container">
<li
v-for="icon in filteredSvgIcons"
:key="'svg-' + icon"
class="icon-item"
@click="selectIcon(icon)"
>
<el-tooltip :content="icon" placement="bottom" effect="light">
<svg-icon :icon-class="icon" />
</el-tooltip>
</li>
</ul>
</el-scrollbar>
</el-tab-pane>
<el-tab-pane label="Element 图标" name="element">
<el-scrollbar height="300px">
<ul class="icon-container">
<li
v-for="icon in filteredEpIcons"
:key="icon"
class="icon-item"
@click="selectIcon(icon)"
>
<el-icon>
<component :is="icon" />
</el-icon>
</li>
</ul>
</el-scrollbar>
</el-tab-pane>
</el-tabs>
</div>
</el-popover>
</div>
</template>
<script setup lang="ts">
import * as ElementPlusIconsVue from "@element-plus/icons-vue";
const props = defineProps({
modelValue: {
type: String,
require: false,
default: "",
},
width: {
type: String,
require: false,
default: "500px",
},
});
const emit = defineEmits(["update:modelValue"]);
const selectedIcon = toRef(props, "modelValue");
const iconSelectRef = ref();
const popoverContentRef = ref();
const activeTab = ref("svg"); // Tab
const searchText = ref(""); //
const popoverVisible = ref(false); //
const svgIcons: string[] = []; // SVG
const filteredSvgIcons = ref<string[]>([]); // SVG
const epIcons: string[] = Object.keys(ElementPlusIconsVue); // Element Plus
const filteredEpIcons = ref<string[]>([]); // Element Plus
onMounted(() => {
loadIcons();
});
/**
* icon 加载
*/
function loadIcons() {
const icons = import.meta.glob("../../assets/icons/*.svg");
for (const path in icons) {
const iconName = path.replace(/.*\/(.*)\.svg$/, "$1");
svgIcons.push(iconName);
}
filteredSvgIcons.value = svgIcons;
}
/**
* 选项卡切换
*/
function handleTabClick(tabPane: any) {
activeTab.value = tabPane.name;
filterIcons();
}
/**
* icon 筛选
*/
function filterIcons() {
if (activeTab.value === "svg") {
// SVG
filteredSvgIcons.value = searchText.value
? svgIcons.filter((iconName) =>
iconName.toLowerCase().includes(searchText.value.toLowerCase())
)
: svgIcons;
} else {
// Element Plus TODO
filteredEpIcons.value = searchText.value
? epIcons.filter((iconName) =>
iconName.toLowerCase().includes(searchText.value.toLowerCase())
)
: epIcons;
}
}
/**
* 选择图标
*/
function selectIcon(iconName: string) {
if (activeTab.value === "element") {
iconName = "el-icon-" + iconName;
}
emit("update:modelValue", iconName);
popoverVisible.value = false;
}
/**
* 点击容器外的区域关闭弹窗 VueUse onClickOutside
*/
onClickOutside(iconSelectRef, () => (popoverVisible.value = false), {
ignore: [popoverContentRef],
});
</script>
<style scoped lang="scss">
.reference :deep(.el-input__wrapper),
.reference :deep(.el-input__inner) {
cursor: pointer;
}
.icon-container {
display: flex;
flex-wrap: wrap;
.icon-item {
display: flex;
align-items: center;
justify-content: center;
padding: 8px;
margin: 4px;
cursor: pointer;
border: 1px solid #dcdfe6;
border-radius: 4px;
transition: all 0.3s;
}
.icon-item:hover {
border-color: #409eff;
scale: 1.2;
}
}
</style>

View File

@ -0,0 +1,47 @@
<template>
<el-dropdown trigger="click" @command="handleLanguageChange">
<div>
<svg-icon icon-class="language" :size="size" />
</div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
v-for="item in langOptions"
:key="item.value"
:disabled="appStore.language === item.value"
:command="item.value"
>
{{ item.label }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
<script setup lang="ts">
import { useI18n } from "vue-i18n";
import { useAppStore } from "@/store/modules/app";
import { LanguageEnum } from "@/enums/LanguageEnum";
defineProps({
size: {
type: String,
required: false,
},
});
const langOptions = [
{ label: "中文", value: LanguageEnum.ZH_CN },
{ label: "English", value: LanguageEnum.EN },
];
const appStore = useAppStore();
const { locale, t } = useI18n();
function handleLanguageChange(lang: string) {
locale.value = lang;
appStore.changeLanguage(lang);
ElMessage.success(t("langSelect.message.success"));
}
</script>

View File

@ -0,0 +1,548 @@
<template>
<el-card shadow="never" class="table-container">
<!-- 表格工具栏 -->
<div class="flex-x-between mb-[10px]">
<!-- 左侧工具栏 -->
<div>
<template v-for="item in toolbar" :key="item">
<template v-if="typeof item === 'string'">
<!-- 新增 -->
<template v-if="item === 'add'">
<el-button
v-hasPerm="[`${contentConfig.pageName}:${item}`]"
type="success"
icon="plus"
@click="handleToolbar(item)"
>
新增
</el-button>
</template>
<!-- 删除 -->
<template v-else-if="item === 'delete'">
<el-button
v-hasPerm="[`${contentConfig.pageName}:${item}`]"
type="danger"
icon="delete"
:disabled="removeIds.length === 0"
@click="handleToolbar(item)"
>
删除
</el-button>
</template>
<!-- 导出 -->
<template v-else-if="item === 'export'">
<el-button
v-hasPerm="[`${contentConfig.pageName}:${item}`]"
type="primary"
icon="download"
@click="handleToolbar(item)"
>
导出
</el-button>
</template>
</template>
<!-- 其他 -->
<template v-else-if="typeof item === 'object'">
<el-button
v-hasPerm="[`${contentConfig.pageName}:${item.auth}`]"
:icon="item.icon"
type="default"
@click="handleToolbar(item.name)"
>
{{ item.text }}
</el-button>
</template>
</template>
</div>
<!-- 右侧工具栏 -->
<div>
<template v-for="item in defaultToolbar" :key="item">
<!-- 刷新 -->
<template v-if="item === 'refresh'">
<el-button icon="refresh" circle @click="handleToolbar(item)" />
</template>
<!-- 列设置 -->
<template v-else-if="item === 'filter'">
<el-popover placement="bottom" trigger="click">
<template #reference>
<el-button icon="Operation" circle />
</template>
<template v-for="col in cols" :key="col">
<el-checkbox
v-if="col.prop"
v-model="col.show"
:label="col.label"
/>
</template>
</el-popover>
</template>
<!-- 搜索 -->
<template v-else-if="item === 'search'">
<el-button icon="search" circle @click="handleToolbar(item)" />
</template>
</template>
</div>
</div>
<!-- 列表 -->
<el-table
v-loading="loading"
v-bind="contentConfig.table"
:data="pageData"
@selection-change="handleSelectionChange"
>
<template v-for="col in cols" :key="col">
<el-table-column v-if="col.show" v-bind="col">
<template #default="scope">
<!-- 显示图片 -->
<template v-if="col.templet === 'image'">
<template v-if="col.prop">
<template v-if="Array.isArray(scope.row[col.prop])">
<template
v-for="(item, index) in scope.row[col.prop]"
:key="item"
>
<el-image
:src="item"
:preview-src-list="scope.row[col.prop]"
:initial-index="index"
:preview-teleported="true"
:style="`width: ${col.imageWidth ?? 40}px; height: ${col.imageHeight ?? 40}px`"
/>
</template>
</template>
<template v-else>
<el-image
:src="scope.row[col.prop]"
:preview-src-list="[scope.row[col.prop]]"
:preview-teleported="true"
:style="`width: ${col.imageWidth ?? 40}px; height: ${col.imageHeight ?? 40}px`"
/>
</template>
</template>
</template>
<!-- 根据行的selectList属性返回对应列表值 -->
<template v-else-if="col.templet === 'list'">
<template v-if="col.prop">
{{ (col.selectList ?? {})[scope.row[col.prop]] }}
</template>
</template>
<!-- 格式化显示链接 -->
<template v-else-if="col.templet === 'url'">
<template v-if="col.prop">
<el-link
type="primary"
:href="scope.row[col.prop]"
target="_blank"
>
{{ scope.row[col.prop] }}
</el-link>
</template>
</template>
<!-- 生成开关组件 -->
<template v-else-if="col.templet === 'switch'">
<template v-if="col.prop">
<!-- pageData.length>0: 解决el-switch组件会在表格初始化的时候触发一次change事件 -->
<el-switch
v-model="scope.row[col.prop]"
:active-value="col.activeValue ?? 1"
:inactive-value="col.inactiveValue ?? 0"
:inline-prompt="true"
:active-text="col.activeText ?? ''"
:inactive-text="col.inactiveText ?? ''"
:validate-event="false"
:disabled="!hasAuth(`${contentConfig.pageName}:modify`)"
@change="
pageData.length > 0 &&
handleModify(col.prop, scope.row[col.prop], scope.row)
"
/>
</template>
</template>
<!-- 格式化为价格 -->
<template v-else-if="col.templet === 'price'">
<template v-if="col.prop">
{{ `${col.priceFormat ?? "¥"}${scope.row[col.prop]}` }}
</template>
</template>
<!-- 格式化为百分比 -->
<template v-else-if="col.templet === 'percent'">
<template v-if="col.prop"> {{ scope.row[col.prop] }}% </template>
</template>
<!-- 显示图标 -->
<template v-else-if="col.templet === 'icon'">
<template v-if="col.prop">
<template v-if="scope.row[col.prop].startsWith('el-icon-')">
<el-icon>
<component
:is="scope.row[col.prop].replace('el-icon-', '')"
/>
</el-icon>
</template>
<template v-else>
<svg-icon :icon-class="scope.row[col.prop]" />
</template>
</template>
</template>
<!-- 格式化时间 -->
<template v-else-if="col.templet === 'date'">
<template v-if="col.prop">
{{
useDateFormat(
scope.row[col.prop],
col.dateFormat ?? "YYYY-MM-DD HH:mm:ss"
).value
}}
</template>
</template>
<!-- 列操作栏 -->
<template v-else-if="col.templet === 'tool'">
<template
v-for="item in col.operat ?? ['edit', 'delete']"
:key="item"
>
<template v-if="typeof item === 'string'">
<!-- 编辑/删除 -->
<template v-if="item === 'edit' || item === 'delete'">
<el-button
v-hasPerm="[`${contentConfig.pageName}:${item}`]"
:type="item === 'edit' ? 'primary' : 'danger'"
:icon="item"
size="small"
link
@click="
handleOperat({
name: item,
row: scope.row,
column: scope.column,
$index: scope.$index,
})
"
>
{{ item === "edit" ? "编辑" : "删除" }}
</el-button>
</template>
</template>
<!-- 其他 -->
<template v-else-if="typeof item === 'object'">
<el-button
v-hasPerm="[`${contentConfig.pageName}:${item.auth}`]"
:icon="item.icon"
type="primary"
size="small"
link
@click="
handleOperat({
name: item.name,
row: scope.row,
column: scope.column,
$index: scope.$index,
})
"
>
{{ item.text }}
</el-button>
</template>
</template>
</template>
<!-- 自定义 -->
<template v-else-if="col.templet === 'custom'">
<slot
:name="col.slotName ?? col.prop"
:prop="col.prop"
v-bind="scope"
></slot>
</template>
</template>
</el-table-column>
</template>
</el-table>
<!-- 分页 -->
<pagination
v-if="total > 0"
v-model:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="handlePagination"
/>
</el-card>
</template>
<script setup lang="ts">
import { ref, reactive } from "vue";
import { useDateFormat } from "@vueuse/core";
import { hasAuth } from "@/plugins/permission";
import Pagination from "@/components/Pagination/index.vue";
import SvgIcon from "@/components/SvgIcon/index.vue";
import type { TableProps } from "element-plus";
//
export type IObject = Record<string, any>;
//
export interface IOperatData {
name: string;
row: any;
column: any;
$index: number;
}
export interface IContentConfig<T = any> {
// (,sys:user:xxx)
pageName: string;
// table
table?: Omit<TableProps<any>, "data">;
// (promise)
indexAction: (queryParams: T) => Promise<any>;
// (promise)
deleteAction?: (ids: string) => Promise<any>;
// (promise)
exportAction?: (queryParams: T) => Promise<any>;
// (promise)
modifyAction?: (data: {
[key: string]: any;
field: string;
value: boolean | string | number;
}) => Promise<any>;
// (id)
pk?: string;
// (add,delete,export,)
toolbar?: Array<
| "add"
| "delete"
| "export"
| {
auth: string;
icon?: string;
name: string;
text: string;
}
>;
//
defaultToolbar?: ("refresh" | "filter" | "search")[];
// table(templet,operat,slotName)
cols: Array<{
type?: "default" | "selection" | "index" | "expand";
label?: string;
prop?: string;
width?: string | number;
align?: "left" | "center" | "right";
show?: boolean;
templet?:
| "image"
| "list"
| "url"
| "switch"
| "price"
| "percent"
| "icon"
| "date"
| "tool"
| "custom";
imageWidth?: number;
imageHeight?: number;
selectList?: Record<string, any>;
activeValue?: boolean | string | number;
inactiveValue?: boolean | string | number;
activeText?: string;
inactiveText?: string;
priceFormat?: string;
dateFormat?: string;
operat?: Array<
| "edit"
| "delete"
| {
auth: string;
icon?: string;
name: string;
text: string;
}
>;
[key: string]: any;
}>;
}
const props = defineProps<{
contentConfig: IContentConfig;
}>();
//
const emit = defineEmits<{
addClick: [];
exportClick: [];
searchClick: [];
toolbarClick: [name: string];
editClick: [row: IObject];
operatClick: [data: IOperatData];
}>();
//
const pk = props.contentConfig.pk ?? "id";
//
const toolbar = props.contentConfig.toolbar ?? ["add", "delete"];
//
const defaultToolbar = props.contentConfig.defaultToolbar ?? [
"refresh",
"filter",
"search",
];
//
const cols = ref(
props.contentConfig.cols.map((col) => {
if (col.show === undefined) {
col.show = true;
}
return col;
})
);
//
const loading = ref(false);
// ID
const removeIds = ref<(number | string)[]>([]);
//
const total = ref(0);
//
const pageData = ref<IObject[]>([]);
//
const pageSize = 10;
//
const queryParams = reactive<IObject>({
pageNum: 1,
pageSize: pageSize,
});
//
let lastFormData = {};
//
function fetchPageData(formData: IObject = {}, isRestart = false) {
loading.value = true;
lastFormData = formData;
if (isRestart) {
queryParams.pageNum = 1;
queryParams.pageSize = pageSize;
}
props.contentConfig
.indexAction({ ...queryParams, ...formData })
.then((data) => {
total.value = data.total;
pageData.value = data.list;
})
.finally(() => {
loading.value = false;
});
}
fetchPageData();
//
function handleSelectionChange(selection: any[]) {
removeIds.value = selection.map((item) => item[pk]);
}
//
function handleRefresh() {
fetchPageData({}, true);
}
//
function handleDelete(id?: number | string) {
const ids = [id || removeIds.value].join(",");
if (!ids) {
ElMessage.warning("请勾选删除项");
return;
}
ElMessageBox.confirm("确认删除?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
}).then(function () {
if (props.contentConfig.deleteAction) {
props.contentConfig.deleteAction(ids).then(() => {
ElMessage.success("删除成功");
handleRefresh();
});
} else {
ElMessage.error("未配置deleteAction");
}
});
}
//
function handlePagination() {
fetchPageData(lastFormData);
}
//
function handleToolbar(name: string) {
switch (name) {
case "refresh":
handleRefresh();
break;
case "search":
emit("searchClick");
break;
case "add":
emit("addClick");
break;
case "delete":
handleDelete();
break;
case "export":
emit("exportClick");
break;
default:
emit("toolbarClick", name);
break;
}
}
//
function handleOperat(data: IOperatData) {
switch (data.name) {
case "edit":
emit("editClick", data.row);
break;
case "delete":
handleDelete(data.row[pk]);
break;
default:
emit("operatClick", data);
break;
}
}
// Excel
function exportPageData(queryParams: IObject = {}) {
if (props.contentConfig.exportAction) {
props.contentConfig.exportAction(queryParams).then((response) => {
const fileData = response.data;
const fileName = decodeURI(
response.headers["content-disposition"].split(";")[1].split("=")[1]
);
const fileType =
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8";
const blob = new Blob([fileData], { type: fileType });
const downloadUrl = window.URL.createObjectURL(blob);
const downloadLink = document.createElement("a");
downloadLink.href = downloadUrl;
downloadLink.download = fileName;
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
window.URL.revokeObjectURL(downloadUrl);
});
} else {
ElMessage.error("未配置exportAction");
}
}
//
function handleModify(
field: string,
value: boolean | string | number,
row: Record<string, any>
) {
if (props.contentConfig.modifyAction) {
props.contentConfig.modifyAction({
[pk]: row[pk],
field: field,
value: value,
});
} else {
ElMessage.error("未配置modifyAction");
}
}
//
defineExpose({ fetchPageData, exportPageData });
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,169 @@
<template>
<el-form
ref="formRef"
label-width="auto"
v-bind="form"
:model="formData"
:rules="formRules"
>
<template v-for="item in formItems" :key="item.prop">
<el-form-item v-show="!item.hidden" :label="item.label" :prop="item.prop">
<!-- Label -->
<template #label v-if="item.tips">
<span>
{{ item.label }}
<el-tooltip
placement="bottom"
effect="light"
:content="item.tips"
:raw-content="true"
>
<el-icon style="vertical-align: -0.15em" size="16">
<QuestionFilled />
</el-icon>
</el-tooltip>
</span>
</template>
<!-- Input 输入框 -->
<template v-if="item.type === 'input' || item.type === undefined">
<el-input v-model="formData[item.prop]" v-bind="item.attrs" />
</template>
<!-- Select 选择器 -->
<template v-else-if="item.type === 'select'">
<el-select v-model="formData[item.prop]" v-bind="item.attrs">
<template v-for="option in item.options" :key="option.value">
<el-option v-bind="option" />
</template>
</el-select>
</template>
<!-- Radio 单选框 -->
<template v-else-if="item.type === 'radio'">
<el-radio-group v-model="formData[item.prop]" v-bind="item.attrs">
<template v-for="option in item.options" :key="option.value">
<el-radio v-bind="option" />
</template>
</el-radio-group>
</template>
<!-- Checkbox 多选框 -->
<template v-else-if="item.type === 'checkbox'">
<el-checkbox-group v-model="formData[item.prop]" v-bind="item.attrs">
<template v-for="option in item.options" :key="option.value">
<el-checkbox v-bind="option" />
</template>
</el-checkbox-group>
</template>
<!-- Input Number 数字输入框 -->
<template v-else-if="item.type === 'input-number'">
<el-input-number v-model="formData[item.prop]" v-bind="item.attrs" />
</template>
<!-- TreeSelect 树形选择 -->
<template v-else-if="item.type === 'tree-select'">
<el-tree-select v-model="formData[item.prop]" v-bind="item.attrs" />
</template>
<!-- DatePicker 日期选择器 -->
<template v-else-if="item.type === 'date-picker'">
<el-date-picker v-model="formData[item.prop]" v-bind="item.attrs" />
</template>
<!-- Text 文本 -->
<template v-else-if="item.type === 'text'">
<el-text v-bind="item.attrs">{{ formData[item.prop] }}</el-text>
</template>
<!-- 自定义 -->
<template v-else-if="item.type === 'custom'">
<slot
:name="item.slotName ?? item.prop"
:prop="item.prop"
:formData="formData"
:attrs="item.attrs"
></slot>
</template>
</el-form-item>
</template>
</el-form>
</template>
<script setup lang="ts">
import type { FormInstance, FormRules } from "element-plus";
import { reactive, ref, watch, computed, watchEffect } from "vue";
import { IForm, IFormItems, IObject } from "./types";
//
const props = withDefaults(
defineProps<{
// (,id)
pk?: string;
// form
form?: IForm;
//
formItems: IFormItems;
}>(),
{
pk: "id",
}
);
const formRef = ref<FormInstance>();
const formData = reactive<IObject>({});
const formRules: FormRules = {};
const watchArr = [];
const computedArr = [];
const watchEffectArr = [];
//
for (const item of props.formItems) {
formData[item.prop] = item.initialValue ?? "";
formRules[item.prop] = item.rules ?? [];
if (item.watch !== undefined) {
watchArr.push({ field: item.prop, func: item.watch });
}
if (item.computed !== undefined) {
computedArr.push({ field: item.prop, func: item.computed });
}
if (item.watchEffect !== undefined) {
watchEffectArr.push(item.watchEffect);
}
}
watchArr.forEach(({ field, func }) => {
watch(
() => formData[field],
(newValue, oldValue) => {
func(newValue, oldValue, formData);
}
);
});
computedArr.forEach(({ field, func }) => {
formData[field] = computed({
get() {
return func(formData);
},
// TODO
set() {},
});
});
watchEffectArr.forEach((func) => {
watchEffect(() => {
func(formData);
});
});
//
function getFormData(key?: string) {
return key === undefined ? formData : formData[key] ?? undefined;
}
//
function setFormData(data: IObject) {
for (const key in formData) {
if (Object.hasOwn(formData, key) && key in data) {
formData[key] = data[key];
}
}
if (Object.hasOwn(data, props.pk)) {
formData[props.pk] = data[props.pk];
}
}
//
function setFormItemData(key: string, value: any) {
formData[key] = value;
}
//
defineExpose({ formRef, getFormData, setFormData, setFormItemData });
</script>

View File

@ -0,0 +1,144 @@
<template>
<!-- drawer -->
<template v-if="modalConfig.component === 'drawer'">
<el-drawer
v-model="modalVisible"
:append-to-body="true"
v-bind="modalConfig.drawer"
@open="handleOpenModal"
@close="handleCloseModal"
>
<!-- 表单 -->
<page-form
ref="pageFormRef"
:pk="modalConfig.pk"
:form="modalConfig.form"
:form-items="modalConfig.formItems"
/>
<!-- 弹窗底部操作按钮 -->
<template #footer>
<div>
<el-button type="primary" @click="handleSubmit"> </el-button>
<el-button @click="handleCloseModal"> </el-button>
</div>
</template>
</el-drawer>
</template>
<!-- dialog -->
<template v-else>
<el-dialog
v-model="modalVisible"
:align-center="true"
:append-to-body="true"
width="70vw"
v-bind="modalConfig.dialog"
style="padding-right: 0"
@open="handleOpenModal"
@close="handleCloseModal"
>
<!-- 滚动 -->
<el-scrollbar max-height="65vh">
<!-- 表单 -->
<page-form
ref="pageFormRef"
:pk="modalConfig.pk"
:form="modalConfig.form"
:form-items="modalConfig.formItems"
style="padding-right: var(--el-dialog-padding-primary)"
/>
</el-scrollbar>
<!-- 弹窗底部操作按钮 -->
<template #footer>
<div style="padding-right: var(--el-dialog-padding-primary)">
<el-button type="primary" @click="handleSubmit"> </el-button>
<el-button @click="handleCloseModal"> </el-button>
</div>
</template>
</el-dialog>
</template>
</template>
<script setup lang="ts">
import { useThrottleFn } from "@vueuse/core";
import { ref } from "vue";
import PageForm from "./Form.vue";
import { IDialog, IDrawer, IForm, IFormItems, IObject } from "./types";
//
export interface IModalConfig<T = any> {
//
pageName?: string;
// (,id)
pk?: string;
//
component?: "dialog" | "drawer";
// dialog
dialog?: IDialog;
// drawer
drawer?: IDrawer;
// form
form?: IForm;
//
formItems: IFormItems<T>;
//
beforeSubmit?: (data: T) => void;
// (promise)
formAction: (data: T) => Promise<any>;
}
const props = defineProps<{
modalConfig: IModalConfig;
}>();
//
const emit = defineEmits<{
submitClick: [];
}>();
const modalVisible = ref(false);
const pageFormRef = ref<InstanceType<typeof PageForm>>();
let initialFormData = {};
// modal
function setModalVisible(data: IObject = {}) {
modalVisible.value = true;
initialFormData = data;
}
//
const handleSubmit = useThrottleFn(() => {
pageFormRef.value?.formRef?.validate((valid: boolean) => {
if (valid) {
const formData = pageFormRef.value?.getFormData();
if (typeof props.modalConfig.beforeSubmit === "function") {
props.modalConfig.beforeSubmit(formData);
}
props.modalConfig.formAction(formData).then(() => {
let msg = "操作成功";
if (props.modalConfig.component === "drawer") {
if (props.modalConfig.drawer?.title) {
msg = props.modalConfig.drawer?.title;
}
} else {
if (props.modalConfig.dialog?.title) {
msg = props.modalConfig.dialog?.title;
}
}
ElMessage.success(msg);
emit("submitClick");
handleCloseModal();
});
}
});
}, 3000);
//
function handleOpenModal() {
pageFormRef.value?.setFormData(initialFormData);
}
//
function handleCloseModal() {
modalVisible.value = false;
pageFormRef.value?.formRef?.resetFields();
pageFormRef.value?.formRef?.clearValidate();
}
//
defineExpose({ setModalVisible });
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,62 @@
import type {
DialogProps,
DrawerProps,
FormProps,
FormItemRule,
} from "element-plus";
// dialog组件属性
export type IDialog = Partial<Omit<DialogProps, "modelValue">>;
// drawer组件属性
export type IDrawer = Partial<Omit<DrawerProps, "modelValue">>;
// form组件属性
export type IForm = Partial<Omit<FormProps, "model" | "rules">>;
// 对象类型
export type IObject = Record<string, any>;
// 表单项
export type IFormItems<T = any> = Array<{
// 组件类型(如input,select,radio,custom等默认input)
type?:
| "input"
| "select"
| "radio"
| "checkbox"
| "tree-select"
| "date-picker"
| "input-number"
| "text"
| "custom";
// 组件属性
attrs?: IObject;
// 组件可选项(适用于select,radio,checkbox组件)
options?: Array<{
label: string;
value: any;
disabled?: boolean;
[key: string]: any;
}>;
// 插槽名(适用于组件类型为custom)
slotName?: string;
// 标签文本
label: string;
// 标签提示
tips?: string;
// 键名
prop: string;
// 验证规则
rules?: FormItemRule[];
// 初始值
initialValue?: any;
// 是否隐藏
hidden?: boolean;
// 监听函数
watch?: (newValue: any, oldValue: any, data: T) => void;
// 计算属性函数
computed?: (data: T) => any;
// 监听收集函数
watchEffect?: (data: T) => void;
}>;

View File

@ -0,0 +1,162 @@
<template>
<div
class="search-container"
v-show="visible"
v-hasPerm="[`${searchConfig.pageName}:query`]"
>
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<template
v-for="(item, index) in searchConfig.formItems"
:key="item.prop"
>
<el-form-item
v-show="isExpand ? true : index < showNumber"
:label="item.label"
:prop="item.prop"
>
<!-- Input 输入框 -->
<template v-if="item.type === 'input' || item.type === undefined">
<el-input
v-model="queryParams[item.prop]"
v-bind="item.attrs"
@keyup.enter="handleQuery"
/>
</template>
<!-- Select 选择器 -->
<template v-else-if="item.type === 'select'">
<el-select v-model="queryParams[item.prop]" v-bind="item.attrs">
<template v-for="option in item.options" :key="option.value">
<el-option :label="option.label" :value="option.value" />
</template>
</el-select>
</template>
<!-- TreeSelect 树形选择 -->
<template v-else-if="item.type === 'tree-select'">
<el-tree-select
v-model="queryParams[item.prop]"
v-bind="item.attrs"
/>
</template>
<!-- DatePicker 日期选择器 -->
<template v-else-if="item.type === 'date-picker'">
<el-date-picker
v-model="queryParams[item.prop]"
v-bind="item.attrs"
/>
</template>
</el-form-item>
</template>
<el-form-item>
<el-button type="primary" icon="search" @click="handleQuery">
搜索
</el-button>
<el-button icon="refresh" @click="handleReset"></el-button>
<!-- 展开/收起 -->
<el-link
v-if="isExpandable && searchConfig.formItems.length > showNumber"
class="ml-2"
type="primary"
:underline="false"
@click="isExpand = !isExpand"
>
<template v-if="isExpand"> <i-ep-arrow-up /> </template>
<template v-else> <i-ep-arrow-down /> </template>
</el-link>
</el-form-item>
</el-form>
</div>
</template>
<script setup lang="ts">
import type { FormInstance } from "element-plus";
import { reactive, ref } from "vue";
//
type IObject = Record<string, any>;
//
export interface ISearchConfig {
// (,sys:user:xxx)
pageName: string;
//
formItems: Array<{
// (input,select)
type?: "input" | "select" | "tree-select" | "date-picker";
//
label: string;
//
prop: string;
//
attrs?: IObject;
//
initialValue?: any;
// (select)
options?: { label: string; value: any }[];
}>;
//
isExpandable?: boolean;
//
showNumber?: number;
}
interface IProps {
searchConfig: ISearchConfig;
}
const props = defineProps<IProps>();
//
const emit = defineEmits<{
queryClick: [queryParams: IObject];
resetClick: [queryParams: IObject];
}>();
//
const visible = ref(true);
// /
const isExpandable = ref(props.searchConfig.isExpandable ?? true);
//
const isExpand = ref(false);
//
const showNumber = computed(() => {
if (isExpandable.value === true) {
return props.searchConfig.showNumber ?? 3;
} else {
return props.searchConfig.formItems.length;
}
});
const queryFormRef = ref<FormInstance>();
//
const queryParams = reactive<IObject>({});
for (const item of props.searchConfig.formItems) {
queryParams[item.prop] = item.initialValue ?? "";
}
//
function handleReset() {
queryFormRef.value?.resetFields();
emit("resetClick", queryParams);
}
//
function handleQuery() {
emit("queryClick", queryParams);
}
//
function getQueryParams() {
return queryParams;
}
// / SearchForm
function toggleVisible() {
visible.value = !visible.value;
}
//
defineExpose({ getQueryParams, toggleVisible });
</script>
<style lang="scss" scoped>
.search-container {
padding: 18px 0 0 10px;
margin-bottom: 10px;
background-color: var(--el-bg-color-overlay);
border: 1px solid var(--el-border-color-light);
border-radius: 4px;
box-shadow: none;
}
</style>

View File

@ -0,0 +1,81 @@
<template>
<el-scrollbar>
<div :class="{ hidden: hidden }" class="pagination">
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:background="background"
:layout="layout"
:page-sizes="pageSizes"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</el-scrollbar>
</template>
<script setup lang="ts">
const props = defineProps({
total: {
required: true,
type: Number as PropType<number>,
default: 0,
},
page: {
type: Number,
default: 1,
},
limit: {
type: Number,
default: 20,
},
pageSizes: {
type: Array as PropType<number[]>,
default() {
return [10, 20, 30, 50];
},
},
layout: {
type: String,
default: "total, sizes, prev, pager, next, jumper",
},
background: {
type: Boolean,
default: true,
},
autoScroll: {
type: Boolean,
default: true,
},
hidden: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(["pagination", "update:page", "update:limit"]);
const currentPage = useVModel(props, "page", emit);
const pageSize = useVModel(props, "limit", emit);
function handleSizeChange(val: number) {
emit("pagination", { page: currentPage, limit: val });
}
function handleCurrentChange(val: number) {
currentPage.value = val;
emit("pagination", { page: val, limit: props.limit });
}
</script>
<style lang="scss" scoped>
.pagination {
padding: 12px;
&.hidden {
display: none;
}
}
</style>

View File

@ -0,0 +1,39 @@
<template>
<el-dropdown trigger="click" @command="handleSizeChange">
<div>
<svg-icon icon-class="size" />
</div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
v-for="item of sizeOptions"
:key="item.value"
:disabled="appStore.size == item.value"
:command="item.value"
>
{{ item.label }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
<script setup lang="ts">
import { SizeEnum } from "@/enums/SizeEnum";
import { useAppStore } from "@/store/modules/app";
const { t } = useI18n();
const sizeOptions = computed(() => {
return [
{ label: t("sizeSelect.default"), value: SizeEnum.DEFAULT },
{ label: t("sizeSelect.large"), value: SizeEnum.LARGE },
{ label: t("sizeSelect.small"), value: SizeEnum.SMALL },
];
});
const appStore = useAppStore();
function handleSizeChange(size: string) {
appStore.changeSize(size);
ElMessage.success(t("sizeSelect.message.success"));
}
</script>

View File

@ -0,0 +1,45 @@
<template>
<svg
aria-hidden="true"
class="svg-icon"
:style="'width:' + size + ';height:' + size"
>
<use :xlink:href="symbolId" :fill="color" />
</svg>
</template>
<script setup lang="ts">
const props = defineProps({
prefix: {
type: String,
default: "icon",
},
iconClass: {
type: String,
required: false,
default: "",
},
color: {
type: String,
default: "",
},
size: {
type: String,
default: "1em",
},
});
const symbolId = computed(() => `#${props.prefix}-${props.iconClass}`);
</script>
<style scoped>
.svg-icon {
display: inline-block;
width: 1em;
height: 1em;
overflow: hidden;
vertical-align: -0.15em; /* 因icon大小被设置为和字体大小一致而span等标签的下边缘会和字体的基线对齐故需设置一个往下的偏移比例来纠正视觉上的未对齐效果 */
outline: none;
fill: currentcolor; /* 定义元素的颜色currentColor是一个变量这个变量的值就表示当前元素的color值如果当前元素未设置color值则从父元素继承 */
}
</style>

View File

@ -0,0 +1,371 @@
<template>
<div ref="tableSelectRef" :style="'width:' + width">
<el-popover
:visible="popoverVisible"
:width="popoverWidth"
placement="bottom-end"
v-bind="selectConfig.popover"
@show="handleShow"
>
<template #reference>
<div @click="popoverVisible = !popoverVisible">
<slot>
<el-input
class="reference"
:model-value="text"
:readonly="true"
:placeholder="placeholder"
>
<template #suffix>
<el-icon
:style="{
transform: popoverVisible ? 'rotate(180deg)' : 'rotate(0)',
transition: 'transform .5s',
}"
>
<ArrowDown />
</el-icon>
</template>
</el-input>
</slot>
</div>
</template>
<!-- 弹出框内容 -->
<div ref="popoverContentRef">
<!-- 表单 -->
<el-form ref="formRef" :model="queryParams" :inline="true">
<template v-for="item in selectConfig.formItems" :key="item.prop">
<el-form-item :label="item.label" :prop="item.prop">
<!-- Input 输入框 -->
<template v-if="item.type === 'input'">
<template v-if="item.attrs?.type === 'number'">
<el-input
v-model.number="queryParams[item.prop]"
v-bind="item.attrs"
@keyup.enter="handleQuery"
/>
</template>
<template v-else>
<el-input
v-model="queryParams[item.prop]"
v-bind="item.attrs"
@keyup.enter="handleQuery"
/>
</template>
</template>
<!-- Select 选择器 -->
<template v-else-if="item.type === 'select'">
<el-select v-model="queryParams[item.prop]" v-bind="item.attrs">
<template v-for="option in item.options" :key="option.value">
<el-option :label="option.label" :value="option.value" />
</template>
</el-select>
</template>
<!-- TreeSelect 树形选择 -->
<template v-else-if="item.type === 'tree-select'">
<el-tree-select
v-model="queryParams[item.prop]"
v-bind="item.attrs"
/>
</template>
<!-- DatePicker 日期选择器 -->
<template v-else-if="item.type === 'date-picker'">
<el-date-picker
v-model="queryParams[item.prop]"
v-bind="item.attrs"
/>
</template>
<!-- Input 输入框 -->
<template v-else>
<template v-if="item.attrs?.type === 'number'">
<el-input
v-model.number="queryParams[item.prop]"
v-bind="item.attrs"
@keyup.enter="handleQuery"
/>
</template>
<template v-else>
<el-input
v-model="queryParams[item.prop]"
v-bind="item.attrs"
@keyup.enter="handleQuery"
/>
</template>
</template>
</el-form-item>
</template>
<el-form-item>
<el-button type="primary" icon="search" @click="handleQuery">
搜索
</el-button>
<el-button icon="refresh" @click="handleReset"></el-button>
</el-form-item>
</el-form>
<!-- 列表 -->
<el-table
ref="tableRef"
v-loading="loading"
:data="pageData"
:border="true"
:max-height="250"
:row-key="pk"
:highlight-current-row="true"
:class="{ radio: !isMultiple }"
@select="handleSelect"
@select-all="handleSelectAll"
>
<template v-for="col in selectConfig.tableColumns" :key="col.prop">
<!-- 自定义 -->
<template v-if="col.templet === 'custom'">
<el-table-column v-bind="col">
<template #default="scope">
<slot
:name="col.slotName ?? col.prop"
:prop="col.prop"
v-bind="scope"
></slot>
</template>
</el-table-column>
</template>
<!-- 其他 -->
<template v-else>
<el-table-column v-bind="col" />
</template>
</template>
</el-table>
<!-- 分页 -->
<pagination
v-if="total > 0"
v-model:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="handlePagination"
/>
<div class="feedback">
<el-button type="primary" size="small" @click="handleConfirm">
{{ confirmText }}
</el-button>
<el-button size="small" @click="handleClear"> </el-button>
<el-button size="small" @click="handleClose"> </el-button>
</div>
</div>
</el-popover>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, computed } from "vue";
import { onClickOutside, useResizeObserver } from "@vueuse/core";
import type { FormInstance, PopoverProps, TableInstance } from "element-plus";
//
export type IObject = Record<string, any>;
//
export interface ISelectConfig<T = any> {
//
width?: string;
//
placeholder?: string;
// popover
popover?: Partial<Omit<PopoverProps, "visible" | "v-model:visible">>;
// (promise)
indexAction: (queryParams: T) => Promise<any>;
// (,id)
pk?: string;
//
multiple?: boolean;
//
formItems: Array<{
// (input,select)
type?: "input" | "select" | "tree-select" | "date-picker";
//
label: string;
//
prop: string;
//
attrs?: IObject;
//
initialValue?: any;
// (select)
options?: { label: string; value: any }[];
}>;
//
tableColumns: Array<{
type?: "default" | "selection" | "index" | "expand";
label?: string;
prop?: string;
width?: string | number;
[key: string]: any;
}>;
}
const props = withDefaults(
defineProps<{
selectConfig: ISelectConfig;
text?: string;
}>(),
{
text: "",
}
);
//
const emit = defineEmits<{
confirmClick: [selection: any[]];
}>();
//
const pk = props.selectConfig.pk ?? "id";
//
const isMultiple = props.selectConfig.multiple === true;
//
const width = props.selectConfig.width ?? "100%";
//
const placeholder = props.selectConfig.placeholder ?? "请选择";
//
const popoverVisible = ref(false);
//
const loading = ref(false);
//
const total = ref(0);
//
const pageData = ref<IObject[]>([]);
//
const pageSize = 10;
//
const queryParams = reactive<{
pageNum: number;
pageSize: number;
[key: string]: any;
}>({
pageNum: 1,
pageSize: pageSize,
});
// popover
const tableSelectRef = ref();
const popoverWidth = ref(width);
useResizeObserver(tableSelectRef, (entries) => {
popoverWidth.value = `${entries[0].contentRect.width}px`;
});
//
const formRef = ref<FormInstance>();
//
for (const item of props.selectConfig.formItems) {
queryParams[item.prop] = item.initialValue ?? "";
}
//
function handleReset() {
formRef.value?.resetFields();
fetchPageData(true);
}
//
function handleQuery() {
fetchPageData(true);
}
//
function fetchPageData(isRestart = false) {
loading.value = true;
if (isRestart) {
queryParams.pageNum = 1;
queryParams.pageSize = pageSize;
}
props.selectConfig
.indexAction(queryParams)
.then((data) => {
total.value = data.total;
pageData.value = data.list;
})
.finally(() => {
loading.value = false;
});
}
//
const tableRef = ref<TableInstance>();
//
for (const item of props.selectConfig.tableColumns) {
if (item.type === "selection") {
item.reserveSelection = true;
break;
}
}
//
const selectedItems = ref<IObject[]>([]);
const confirmText = computed(() => {
return selectedItems.value.length > 0
? `已选(${selectedItems.value.length})`
: "确 定";
});
function handleSelect(selection: any[], row: any) {
if (isMultiple || selection.length === 0) {
//
selectedItems.value = selection;
} else {
//
selectedItems.value = [selection[selection.length - 1]];
tableRef.value?.clearSelection();
tableRef.value?.toggleRowSelection(selectedItems.value[0], true);
tableRef.value?.setCurrentRow(selectedItems.value[0]);
}
}
function handleSelectAll(selection: any[]) {
if (isMultiple) {
selectedItems.value = selection;
}
}
//
function handlePagination() {
fetchPageData();
}
//
const isInit = ref(false);
//
function handleShow() {
if (isInit.value === false) {
isInit.value = true;
fetchPageData();
}
}
//
function handleConfirm() {
if (selectedItems.value.length === 0) {
ElMessage.error("请选择数据");
return;
}
popoverVisible.value = false;
emit("confirmClick", selectedItems.value);
}
//
function handleClear() {
tableRef.value?.clearSelection();
selectedItems.value = [];
}
//
function handleClose() {
popoverVisible.value = false;
}
const popoverContentRef = ref();
/* onClickOutside(tableSelectRef, () => (popoverVisible.value = false), {
ignore: [popoverContentRef],
}); */
</script>
<style scoped lang="scss">
.reference :deep(.el-input__wrapper),
.reference :deep(.el-input__inner) {
cursor: pointer;
}
.feedback {
display: flex;
justify-content: flex-end;
margin-top: 6px;
}
//
.radio :deep(.el-table__header th.el-table__cell:nth-child(1) .el-checkbox) {
visibility: hidden;
}
</style>

View File

@ -0,0 +1,134 @@
<!-- 多图上传组件 -->
<template>
<el-upload
v-model:file-list="fileList"
list-type="picture-card"
:before-upload="handleBeforeUpload"
:http-request="handleUpload"
:on-remove="handleRemove"
:on-preview="previewImg"
:limit="props.limit"
>
<i-ep-plus />
</el-upload>
<el-dialog v-model="dialogVisible">
<img w-full :src="previewImgUrl" alt="Preview Image" />
</el-dialog>
</template>
<script setup lang="ts">
import {
UploadRawFile,
UploadRequestOptions,
UploadUserFile,
UploadFile,
UploadProps,
} from "element-plus";
import FileAPI from "@/api/file";
const emit = defineEmits(["update:modelValue"]);
const props = defineProps({
/**
* 文件路径集合
*/
modelValue: {
type: Array<string>,
default: () => [],
},
/**
* 文件上传数量限制
*/
limit: {
type: Number,
default: 10,
},
});
const previewImgUrl = ref("");
const dialogVisible = ref(false);
const fileList = ref([] as UploadUserFile[]);
watch(
() => props.modelValue,
(newVal: string[]) => {
const filePaths = fileList.value.map((file) => file.url);
// modelValue
if (
filePaths.length > 0 &&
filePaths.length === newVal.length &&
filePaths.every((x) => newVal.some((y) => y === x)) &&
newVal.every((y) => filePaths.some((x) => x === y))
) {
return;
}
fileList.value = newVal.map((filePath) => {
return { url: filePath } as UploadUserFile;
});
},
{ immediate: true }
);
/**
* 自定义图片上传
*
* @param params
*/
async function handleUpload(options: UploadRequestOptions): Promise<any> {
// API
const data = await FileAPI.upload(options.file);
// URL blob:http://
const fileIndex = fileList.value.findIndex(
(file) => file.uid == (options.file as any).uid
);
fileList.value.splice(fileIndex, 1, {
name: data.name,
url: data.url,
} as UploadUserFile);
emit(
"update:modelValue",
fileList.value.map((file) => file.url)
);
}
/**
* 删除图片
*/
function handleRemove(removeFile: UploadFile) {
const filePath = removeFile.url;
if (filePath) {
FileAPI.deleteByPath(filePath).then(() => {
//
emit(
"update:modelValue",
fileList.value.map((file) => file.url)
);
});
}
}
/**
* 限制用户上传文件的格式和大小
*/
function handleBeforeUpload(file: UploadRawFile) {
if (file.size > 2 * 1048 * 1048) {
ElMessage.warning("上传图片不能大于2M");
return false;
}
return true;
}
/**
* 预览图片
*/
const previewImg: UploadProps["onPreview"] = (uploadFile) => {
previewImgUrl.value = uploadFile.url!;
dialogVisible.value = true;
};
</script>

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