更新 vue-element-plus-admin 到最新版本 2.5.6
This commit is contained in:
parent
bade36dd1b
commit
9ceeacb97b
@ -1,20 +1,20 @@
|
||||
{
|
||||
"name": "vue-element-plus-admin",
|
||||
"version": "2.3.0",
|
||||
"version": "2.5.6",
|
||||
"description": "一套基于vue3、element-plus、typesScript、vite4的后台集成方案。",
|
||||
"author": "Archer <502431556@qq.com>",
|
||||
"private": false,
|
||||
"scripts": {
|
||||
"i": "pnpm install",
|
||||
"dev": "vite --mode dev",
|
||||
"ts:check": "vue-tsc --noEmit --skipLibCheck",
|
||||
"build:pro": "vite build --mode pro",
|
||||
"build:dev": "vite build --mode dev",
|
||||
"serve:pro": "vite preview --mode pro",
|
||||
"serve:dev": "vite preview --mode dev",
|
||||
"npm:check": "npx npm-check-updates",
|
||||
"clean": "npx rimraf node_modules",
|
||||
"clean:cache": "npx rimraf node_modules/.cache",
|
||||
"dev": "pnpm vite --mode dev",
|
||||
"ts:check": "pnpm vue-tsc --noEmit --skipLibCheck",
|
||||
"build:pro": "pnpm vite build --mode pro",
|
||||
"build:dev": "pnpm vite build --mode dev",
|
||||
"serve:pro": "pnpm vite preview --mode pro",
|
||||
"serve:dev": "pnpm vite preview --mode dev",
|
||||
"npm:check": "pnpx npm-check-updates -u",
|
||||
"clean": "pnpx rimraf node_modules",
|
||||
"clean:cache": "pnpx rimraf node_modules/.cache",
|
||||
"lint:eslint": "eslint --fix --ext .js,.ts,.vue ./src",
|
||||
"lint:format": "prettier --write --loglevel warn \"src/**/*.{js,ts,json,tsx,css,less,vue,html,md}\"",
|
||||
"lint:style": "stylelint --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/",
|
||||
@ -22,100 +22,105 @@
|
||||
"icon": "esno ./scripts/icon.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@amap/amap-jsapi-loader": "1.0.1",
|
||||
"@iconify/iconify": "3.1.1",
|
||||
"@iconify/vue": "4.1.1",
|
||||
"@vueuse/core": "10.3.0",
|
||||
"@vueuse/core": "10.7.1",
|
||||
"@wangeditor/editor": "5.1.23",
|
||||
"@wangeditor/editor-for-vue": "5.1.10",
|
||||
"@zxcvbn-ts/core": "3.0.3",
|
||||
"@zxcvbn-ts/core": "3.0.4",
|
||||
"animate.css": "4.1.1",
|
||||
"axios": "1.4.0",
|
||||
"dayjs": "1.11.9",
|
||||
"driver.js": "1.2.1",
|
||||
"axios": "1.6.5",
|
||||
"cropperjs": "1.6.1",
|
||||
"dayjs": "1.11.10",
|
||||
"driver.js": "1.3.1",
|
||||
"echarts": "5.4.3",
|
||||
"echarts-wordcloud": "2.1.0",
|
||||
"element-plus": "2.3.9",
|
||||
"element-plus": "2.4.4",
|
||||
"lodash-es": "4.17.21",
|
||||
"mitt": "3.0.1",
|
||||
"mockjs": "1.1.0",
|
||||
"nprogress": "0.2.0",
|
||||
"pinia": "2.1.6",
|
||||
"pinia-plugin-persist": "1.0.0",
|
||||
"pinia": "2.1.7",
|
||||
"pinia-plugin-persistedstate": "3.2.1",
|
||||
"qrcode": "1.5.3",
|
||||
"qs": "6.11.2",
|
||||
"url": "0.11.1",
|
||||
"vue": "3.3.4",
|
||||
"vue-i18n": "9.2.2",
|
||||
"vue-json-pretty": "2.2.4",
|
||||
"vue-router": "4.2.4",
|
||||
"vue-types": "5.1.1"
|
||||
"url": "0.11.3",
|
||||
"vue": "3.4.7",
|
||||
"vue-draggable-plus": "0.3.4",
|
||||
"vue-i18n": "9.9.0",
|
||||
"vue-json-pretty": "2.3.0",
|
||||
"vue-router": "4.2.5",
|
||||
"vue-types": "5.1.1",
|
||||
"xgplayer": "3.0.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify/json": "2.2.101",
|
||||
"@intlify/unplugin-vue-i18n": "0.12.2",
|
||||
"@amap/amap-jsapi-loader": "1.0.1",
|
||||
"@commitlint/cli": "18.4.4",
|
||||
"@commitlint/config-conventional": "18.4.4",
|
||||
"@iconify/json": "2.2.166",
|
||||
"@intlify/unplugin-vue-i18n": "2.0.0",
|
||||
"@kjgl77/datav-vue3": "1.6.1",
|
||||
"@purge-icons/generated": "0.9.0",
|
||||
"@types/fs-extra": "11.0.2",
|
||||
"@types/inquirer": "9.0.3",
|
||||
"@types/lodash-es": "4.17.8",
|
||||
"@types/node": "20.4.10",
|
||||
"@types/nprogress": "0.2.0",
|
||||
"@types/qrcode": "1.5.1",
|
||||
"@types/qs": "6.9.7",
|
||||
"@types/sortablejs": "1.15.1",
|
||||
"@typescript-eslint/eslint-plugin": "6.3.0",
|
||||
"@typescript-eslint/parser": "6.3.0",
|
||||
"@unocss/transformer-variant-group": "0.55.0",
|
||||
"@vitejs/plugin-legacy": "4.1.1",
|
||||
"@vitejs/plugin-vue": "4.2.3",
|
||||
"@vitejs/plugin-vue-jsx": "3.0.1",
|
||||
"autoprefixer": "10.4.14",
|
||||
"@types/fs-extra": "11.0.4",
|
||||
"@types/inquirer": "9.0.7",
|
||||
"@types/lodash-es": "4.17.12",
|
||||
"@types/node": "20.10.8",
|
||||
"@types/nprogress": "0.2.3",
|
||||
"@types/qrcode": "1.5.5",
|
||||
"@types/qs": "6.9.11",
|
||||
"@types/sortablejs": "1.15.7",
|
||||
"@typescript-eslint/eslint-plugin": "6.18.1",
|
||||
"@typescript-eslint/parser": "6.18.1",
|
||||
"@unocss/transformer-variant-group": "0.58.3",
|
||||
"@vitejs/plugin-legacy": "5.2.0",
|
||||
"@vitejs/plugin-vue": "5.0.2",
|
||||
"@vitejs/plugin-vue-jsx": "3.1.0",
|
||||
"autoprefixer": "10.4.16",
|
||||
"chalk": "5.3.0",
|
||||
"consola": "3.2.3",
|
||||
"cron-validate": "1.4.5",
|
||||
"eslint": "8.47.0",
|
||||
"eslint-config-prettier": "9.0.0",
|
||||
"eslint-define-config": "1.23.0",
|
||||
"eslint-plugin-prettier": "5.0.0",
|
||||
"eslint-plugin-vue": "9.17.0",
|
||||
"esno": "0.17.0",
|
||||
"fs-extra": "11.1.1",
|
||||
"intro.js": "7.2.0",
|
||||
"inquirer": "9.2.11",
|
||||
"eslint": "8.56.0",
|
||||
"eslint-config-prettier": "9.1.0",
|
||||
"eslint-define-config": "2.1.0",
|
||||
"eslint-plugin-prettier": "5.1.2",
|
||||
"eslint-plugin-vue": "9.19.2",
|
||||
"esno": "4.0.0",
|
||||
"fs-extra": "11.2.0",
|
||||
"inquirer": "9.2.12",
|
||||
"less": "4.2.0",
|
||||
"lint-staged": "13.2.3",
|
||||
"lint-staged": "15.2.0",
|
||||
"lodash": "4.17.21",
|
||||
"moment": "2.29.4",
|
||||
"plop": "3.1.2",
|
||||
"postcss": "8.4.27",
|
||||
"moment": "2.30.1",
|
||||
"plop": "4.0.1",
|
||||
"postcss": "8.4.33",
|
||||
"postcss-html": "1.5.0",
|
||||
"postcss-less": "6.0.0",
|
||||
"prettier": "3.0.1",
|
||||
"rimraf": "5.0.1",
|
||||
"rollup": "3.28.0",
|
||||
"stylelint": "15.10.2",
|
||||
"prettier": "3.1.1",
|
||||
"rimraf": "5.0.5",
|
||||
"rollup": "4.9.4",
|
||||
"rollup-plugin-visualizer": "5.12.0",
|
||||
"stylelint": "16.1.0",
|
||||
"stylelint-config-html": "1.1.0",
|
||||
"stylelint-config-recommended": "13.0.0",
|
||||
"stylelint-config-standard": "34.0.0",
|
||||
"stylelint-order": "6.0.3",
|
||||
"terser": "5.19.2",
|
||||
"typescript": "5.1.6",
|
||||
"unocss": "0.55.0",
|
||||
"vite": "4.4.9",
|
||||
"vite-plugin-ejs": "1.6.4",
|
||||
"stylelint-config-recommended": "14.0.0",
|
||||
"stylelint-config-standard": "36.0.0",
|
||||
"stylelint-order": "6.0.4",
|
||||
"terser": "5.26.0",
|
||||
"typescript": "5.3.3",
|
||||
"unocss": "0.58.3",
|
||||
"vite": "5.0.11",
|
||||
"vite-plugin-ejs": "1.7.0",
|
||||
"vite-plugin-eslint": "1.8.1",
|
||||
"vite-plugin-mock": "2.9.6",
|
||||
"vite-plugin-progress": "0.0.7",
|
||||
"vite-plugin-purge-icons": "0.9.2",
|
||||
"vite-plugin-purge-icons": "0.10.0",
|
||||
"vite-plugin-style-import": "2.0.0",
|
||||
"vite-plugin-svg-icons": "2.0.1",
|
||||
"vue-draggable-plus": "0.2.6",
|
||||
"vue-tsc": "1.8.8",
|
||||
"vue3-json-viewer": "2.2.2"
|
||||
"vue-tsc": "1.8.27",
|
||||
"vue3-json-viewer": "2.2.2",
|
||||
"zipson": "^0.2.12"
|
||||
},
|
||||
"packageManager": "pnpm@8.1.0",
|
||||
"engines": {
|
||||
"node": ">= 14.18.0"
|
||||
"node": ">=18.0.0",
|
||||
"pnpm": ">=8.1.0"
|
||||
},
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
|
@ -2,9 +2,7 @@
|
||||
import { computed } from 'vue'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
import { ConfigGlobal } from '@/components/ConfigGlobal'
|
||||
import { isDark } from '@/utils/is'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
import { useStorage } from '@/hooks/web/useStorage'
|
||||
import { getSystemBaseConfigApi } from '@/api/vadmin/system/settings'
|
||||
|
||||
const { getPrefixCls } = useDesign()
|
||||
@ -17,18 +15,6 @@ const currentSize = computed(() => appStore.getCurrentSize)
|
||||
|
||||
const greyMode = computed(() => appStore.getGreyMode)
|
||||
|
||||
const { getStorage } = useStorage()
|
||||
|
||||
// 根据浏览器当前主题设置系统主题色
|
||||
const setDefaultTheme = () => {
|
||||
if (getStorage('isDark') !== null) {
|
||||
appStore.setIsDark(getStorage('isDark'))
|
||||
return
|
||||
}
|
||||
const isDarkTheme = isDark()
|
||||
appStore.setIsDark(isDarkTheme)
|
||||
}
|
||||
|
||||
// 添加mate标签
|
||||
const addMeta = (name: string, content: string) => {
|
||||
const meta = document.createElement('meta')
|
||||
@ -39,6 +25,9 @@ const addMeta = (name: string, content: string) => {
|
||||
|
||||
// 获取并设置系统配置
|
||||
const setSystemConfig = async () => {
|
||||
if (appStore.getLogoImage) {
|
||||
return
|
||||
}
|
||||
const res = await getSystemBaseConfigApi()
|
||||
if (res) {
|
||||
appStore.setTitle(res.data.web_title || import.meta.env.VITE_APP_TITLE)
|
||||
@ -52,7 +41,7 @@ const setSystemConfig = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
setDefaultTheme()
|
||||
appStore.initTheme()
|
||||
setSystemConfig()
|
||||
</script>
|
||||
|
||||
|
3
kinit-admin/src/components/Button/index.ts
Normal file
3
kinit-admin/src/components/Button/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import BaseButton from './src/Button.vue'
|
||||
|
||||
export { BaseButton }
|
113
kinit-admin/src/components/Button/src/Button.vue
Normal file
113
kinit-admin/src/components/Button/src/Button.vue
Normal file
@ -0,0 +1,113 @@
|
||||
<script setup lang="ts">
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
import { ElButton, ComponentSize, ButtonType } from 'element-plus'
|
||||
import { PropType, Component, computed, unref } from 'vue'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
const appStore = useAppStore()
|
||||
const getTheme = computed(() => appStore.getTheme)
|
||||
const { getPrefixCls } = useDesign()
|
||||
const prefixCls = getPrefixCls('button')
|
||||
const props = defineProps({
|
||||
size: {
|
||||
type: String as PropType<ComponentSize>,
|
||||
default: undefined
|
||||
},
|
||||
type: {
|
||||
type: String as PropType<ButtonType>,
|
||||
default: 'default'
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
plain: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
text: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
bg: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
link: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
round: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
circle: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
loadingIcon: {
|
||||
type: [String, Object] as PropType<String | Component>,
|
||||
default: undefined
|
||||
},
|
||||
icon: {
|
||||
type: [String, Object] as PropType<String | Component>,
|
||||
default: undefined
|
||||
},
|
||||
autofocus: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
nativeType: {
|
||||
type: String as PropType<'button' | 'submit' | 'reset'>,
|
||||
default: 'button'
|
||||
},
|
||||
autoInsertSpace: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
darker: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
tag: {
|
||||
type: [String, Object] as PropType<String | Component>,
|
||||
default: 'button'
|
||||
}
|
||||
})
|
||||
const emits = defineEmits(['click'])
|
||||
const color = computed(() => {
|
||||
const { type, link } = props
|
||||
if (type === 'primary' && !link) {
|
||||
return unref(getTheme).elColorPrimary
|
||||
}
|
||||
return ''
|
||||
})
|
||||
const style = computed(() => {
|
||||
const { type, link } = props
|
||||
if (type === 'primary' && !link) {
|
||||
return '--el-button-text-color: #fff; --el-button-hover-text-color: #fff'
|
||||
}
|
||||
return ''
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElButton
|
||||
:class="`${prefixCls} color-#fff`"
|
||||
v-bind="{ ...props }"
|
||||
:color="color"
|
||||
:style="style"
|
||||
@click="() => emits('click')"
|
||||
>
|
||||
<slot></slot>
|
||||
<slot name="icon"></slot>
|
||||
<slot name="loading"></slot>
|
||||
</ElButton>
|
||||
</template>
|
@ -1,5 +1,5 @@
|
||||
<script lang="tsx">
|
||||
import { ElCollapseTransition, ElDescriptions, ElDescriptionsItem, ElTooltip } from 'element-plus'
|
||||
import { ElCollapseTransition, ElTooltip, ElRow, ElCol } from 'element-plus'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { ref, unref, PropType, computed, defineComponent } from 'vue'
|
||||
@ -16,6 +16,8 @@ const { getPrefixCls } = useDesign()
|
||||
|
||||
const prefixCls = getPrefixCls('descriptions')
|
||||
|
||||
const defaultData = '-'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Descriptions',
|
||||
props: {
|
||||
@ -36,7 +38,7 @@ export default defineComponent({
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
setup(props, { slots, attrs }) {
|
||||
setup(props, { attrs }) {
|
||||
const getBindValue = computed((): any => {
|
||||
const delArr: string[] = ['title', 'message', 'collapse', 'schema', 'data', 'class']
|
||||
const obj = { ...attrs, ...props }
|
||||
@ -59,7 +61,10 @@ export default defineComponent({
|
||||
delete obj[key]
|
||||
}
|
||||
}
|
||||
return obj
|
||||
return {
|
||||
labelClassName: `${prefixCls}-label`,
|
||||
...obj
|
||||
}
|
||||
}
|
||||
|
||||
// 折叠
|
||||
@ -103,26 +108,51 @@ export default defineComponent({
|
||||
|
||||
<ElCollapseTransition>
|
||||
<div v-show={unref(show)} class={[`${prefixCls}-content`, 'p-20px']}>
|
||||
<ElDescriptions {...unref(getBindValue)}>
|
||||
{{
|
||||
extra: () => (slots['extra'] ? slots['extra']() : props.extra),
|
||||
default: () => {
|
||||
return props.schema.map((item) => {
|
||||
return (
|
||||
<ElDescriptionsItem key={item.field} {...getBindItemValue(item)}>
|
||||
{{
|
||||
label: () => (item.slots?.label ? item.slots?.label(item) : item.label),
|
||||
default: () =>
|
||||
item.slots?.default
|
||||
? item.slots?.default(props.data)
|
||||
: get(props.data, item.field)
|
||||
}}
|
||||
</ElDescriptionsItem>
|
||||
)
|
||||
})
|
||||
}
|
||||
}}
|
||||
</ElDescriptions>
|
||||
<ElRow
|
||||
gutter={0}
|
||||
{...unref(getBindValue)}
|
||||
class="outline-1px outline-[var(--el-border-color-lighter)] outline-solid"
|
||||
>
|
||||
{props.schema.map((item) => {
|
||||
return (
|
||||
<ElCol
|
||||
key={item.field}
|
||||
span={item.span || 24 / props.column}
|
||||
class="flex items-stretch"
|
||||
>
|
||||
{props.direction === 'horizontal' ? (
|
||||
<div class="flex items-stretch bg-[var(--el-fill-color-light)] outline-1px outline-[var(--el-border-color-lighter)] outline-solid flex-1">
|
||||
<div
|
||||
{...getBindItemValue(item)}
|
||||
class="w-120px text-left px-8px py-11px font-700 color-[var(--el-text-color-regular)] border-r-1px border-r-[var(--el-border-color-lighter)] border-r-solid "
|
||||
>
|
||||
{item.label}
|
||||
</div>
|
||||
<div class="flex-1 px-8px py-11px bg-[var(--el-bg-color)] color-[var(--el-text-color-primary)] text-size-14px">
|
||||
{item.slots?.default
|
||||
? item.slots?.default(props.data)
|
||||
: get(props.data, item.field) ?? defaultData}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div class="bg-[var(--el-fill-color-light)] outline-1px outline-[var(--el-border-color-lighter)] outline-solid flex-1">
|
||||
<div
|
||||
{...getBindItemValue(item)}
|
||||
class="text-left px-8px py-11px font-700 color-[var(--el-text-color-regular)] border-b-1px border-b-[var(--el-border-color-lighter)] border-b-solid"
|
||||
>
|
||||
{item.label}
|
||||
</div>
|
||||
<div class="flex-1 px-8px py-11px bg-[var(--el-bg-color)] color-[var(--el-text-color-primary)] text-size-14px">
|
||||
{item.slots?.default
|
||||
? item.slots?.default(props.data)
|
||||
: get(props.data, item.field) ?? defaultData}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</ElCol>
|
||||
)
|
||||
})}
|
||||
</ElRow>
|
||||
</div>
|
||||
</ElCollapseTransition>
|
||||
</div>
|
||||
@ -153,9 +183,13 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
|
||||
.@{prefix-cls}-content {
|
||||
:deep(.@{elNamespace}-descriptions__cell) {
|
||||
width: 0;
|
||||
}
|
||||
:deep(.@{prefix-cls}-label) {
|
||||
width: 150px !important;
|
||||
}
|
||||
|
||||
// .@{prefix-cls}-content {
|
||||
// :deep(.@{elNamespace}-descriptions__cell) {
|
||||
// width: 0;
|
||||
// }
|
||||
// }
|
||||
</style>
|
||||
|
@ -10,7 +10,6 @@ const props = defineProps({
|
||||
modelValue: propTypes.bool.def(false),
|
||||
title: propTypes.string.def('Dialog'),
|
||||
fullscreen: propTypes.bool.def(true),
|
||||
|
||||
top: propTypes.string.def('8vh'),
|
||||
height: propTypes.oneOfType([String, Number]).def('500px'),
|
||||
width: propTypes.oneOfType([String, Number]).def('700px')
|
||||
@ -109,26 +108,30 @@ const dialogStyle = computed(() => {
|
||||
</template>
|
||||
|
||||
<style lang="less">
|
||||
// .@{elNamespace}-overlay-dialog {
|
||||
// display: flex;
|
||||
// justify-content: center;
|
||||
// align-items: center;
|
||||
// }
|
||||
.@{elNamespace}-overlay-dialog {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.@{elNamespace}-dialog {
|
||||
// margin: 0 !important;
|
||||
margin: 0 !important;
|
||||
|
||||
&__header {
|
||||
height: 54px;
|
||||
padding: 0;
|
||||
margin-right: 0 !important;
|
||||
border-bottom: 1px solid var(--el-border-color);
|
||||
padding: 0;
|
||||
height: 54px;
|
||||
}
|
||||
|
||||
&__body {
|
||||
padding: 15px !important;
|
||||
}
|
||||
|
||||
&__footer {
|
||||
border-top: 1px solid var(--el-border-color);
|
||||
}
|
||||
|
||||
&__headerbtn {
|
||||
top: 0;
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import networkError from '@/assets/svgs/500.svg'
|
||||
import noPermission from '@/assets/svgs/403.svg'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { ElButton } from 'element-plus'
|
||||
|
||||
interface ErrorMap {
|
||||
url: string
|
||||
@ -51,7 +50,7 @@ const btnClick = () => {
|
||||
<img width="350" :src="errorMap[type].url" alt="" />
|
||||
<div class="text-14px text-[var(--el-color-info)]">{{ errorMap[type].message }}</div>
|
||||
<div class="mt-20px">
|
||||
<ElButton type="primary" @click="btnClick">{{ errorMap[type].buttonText }}</ElButton>
|
||||
<BaseButton type="primary" @click="btnClick">{{ errorMap[type].buttonText }}</BaseButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -15,7 +15,7 @@ const title = computed(() => appStore.getTitle)
|
||||
<template>
|
||||
<div
|
||||
:class="prefixCls"
|
||||
class="text-center text-[var(--el-text-color-placeholder)] bg-[var(--app-content-bg-color)] h-[var(--app-footer-height)] leading-[var(--app-footer-height)] dark:bg-[var(--el-bg-color)]"
|
||||
class="shrink-0 text-center text-[var(--el-text-color-placeholder)] bg-[var(--app-content-bg-color)] h-[var(--app-footer-height)] leading-[var(--app-footer-height)] dark:bg-[var(--el-bg-color)]"
|
||||
>
|
||||
Copyright ©2021-present {{ title }}
|
||||
</div>
|
||||
|
@ -95,9 +95,6 @@ export default defineComponent({
|
||||
// element form 实例
|
||||
const elFormRef = ref<ComponentRef<typeof ElForm>>()
|
||||
|
||||
// useForm传入的props
|
||||
const outsideProps = ref<FormProps>({})
|
||||
|
||||
const mergeProps = ref<FormProps>({})
|
||||
|
||||
const getProps = computed(() => {
|
||||
@ -155,8 +152,6 @@ export default defineComponent({
|
||||
|
||||
const setProps = (props: FormProps = {}) => {
|
||||
mergeProps.value = Object.assign(unref(mergeProps), props)
|
||||
// @ts-ignore
|
||||
outsideProps.value = props
|
||||
}
|
||||
|
||||
const delSchema = (field: string) => {
|
||||
@ -364,13 +359,31 @@ export default defineComponent({
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
return item.component === ComponentNameEnum.UPLOAD ? (
|
||||
<Com
|
||||
vModel:file-list={itemVal.value}
|
||||
ref={(el: any) => setComponentRefMap(el, item.field)}
|
||||
{...(autoSetPlaceholder && setTextPlaceholder(item))}
|
||||
{...setComponentProps(item)}
|
||||
style={
|
||||
item.componentProps?.style || {
|
||||
width: '100%'
|
||||
}
|
||||
}
|
||||
>
|
||||
{{ ...slotsMap }}
|
||||
</Com>
|
||||
) : (
|
||||
<Com
|
||||
vModel={itemVal.value}
|
||||
ref={(el: any) => setComponentRefMap(el, item.field)}
|
||||
{...(autoSetPlaceholder && setTextPlaceholder(item))}
|
||||
{...setComponentProps(item)}
|
||||
style={item.componentProps?.style || {}}
|
||||
style={
|
||||
item.componentProps?.style || {
|
||||
width: '100%'
|
||||
}
|
||||
}
|
||||
>
|
||||
{{ ...slotsMap }}
|
||||
</Com>
|
||||
@ -447,6 +460,10 @@ export default defineComponent({
|
||||
{...getFormBindValue()}
|
||||
model={unref(getProps).isCustom ? unref(getProps).model : formModel}
|
||||
class={prefixCls}
|
||||
// @ts-ignore
|
||||
onSubmit={(e: Event) => {
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
{{
|
||||
// 如果需要自定义,就什么都不渲染,而是提供默认插槽
|
||||
@ -466,4 +483,8 @@ export default defineComponent({
|
||||
margin-right: 0 !important;
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
|
||||
.@{elNamespace}-form--inline .@{elNamespace}-input {
|
||||
width: 245px;
|
||||
}
|
||||
</style>
|
||||
|
@ -25,6 +25,7 @@ import { Editor } from '@/components/Editor'
|
||||
import { Text } from '@/components/Text'
|
||||
import { JsonEditor } from '@/components/JsonEditor'
|
||||
import { ComponentName } from '../types'
|
||||
import { IconPicker } from '@/components/IconPicker'
|
||||
|
||||
const componentMap: Recordable<Component, ComponentName> = {
|
||||
RadioGroup: ElRadioGroup,
|
||||
@ -51,6 +52,7 @@ const componentMap: Recordable<Component, ComponentName> = {
|
||||
TreeSelect: ElTreeSelect,
|
||||
Upload: ElUpload,
|
||||
JsonEditor: JsonEditor,
|
||||
IconPicker: IconPicker,
|
||||
Text: Text
|
||||
}
|
||||
|
||||
|
@ -56,6 +56,7 @@ export enum ComponentNameEnum {
|
||||
TREE_SELECT = 'TreeSelect',
|
||||
UPLOAD = 'Upload',
|
||||
JSON_EDITOR = 'JsonEditor',
|
||||
ICON_PICKER = 'IconPicker',
|
||||
Text = 'Text'
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,11 @@ const symbolId = computed(() => {
|
||||
return unref(isLocal) ? `#icon-${props.icon.split('svg-icon:')[1]}` : props.icon
|
||||
})
|
||||
|
||||
// 是否使用在线图标
|
||||
const isUseOnline = computed(() => {
|
||||
return import.meta.env.VITE_USE_ONLINE_ICON === 'true'
|
||||
})
|
||||
|
||||
const getIconifyStyle = computed(() => {
|
||||
const { color, size } = props
|
||||
return {
|
||||
@ -40,7 +45,10 @@ const getIconifyStyle = computed(() => {
|
||||
<use :xlink:href="symbolId" />
|
||||
</svg>
|
||||
|
||||
<Icon v-else :icon="icon" :style="getIconifyStyle" />
|
||||
<template v-else>
|
||||
<Icon v-if="isUseOnline" :icon="icon" :style="getIconifyStyle" />
|
||||
<div v-else :class="`${icon} iconify`" :style="getIconifyStyle"></div>
|
||||
</template>
|
||||
</ElIcon>
|
||||
</template>
|
||||
|
||||
@ -49,11 +57,18 @@ const getIconifyStyle = computed(() => {
|
||||
|
||||
.@{prefix-cls},
|
||||
.iconify {
|
||||
&:hover {
|
||||
:deep(svg) {
|
||||
:deep(svg) {
|
||||
&:hover {
|
||||
// stylelint-disable-next-line
|
||||
color: v-bind(hoverColor) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.iconify {
|
||||
&:hover {
|
||||
// stylelint-disable-next-line
|
||||
color: v-bind(hoverColor) !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -7,12 +7,6 @@ import { ElInput, ElPopover, ElScrollbar, ElTabs, ElTabPane, ElPagination } from
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
import { computed, CSSProperties, ref, unref, watch } from 'vue'
|
||||
import { nextTick } from 'vue'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
|
||||
defineProps({
|
||||
// 是否显示密码强度
|
||||
inputDisabled: propTypes.bool.def(false)
|
||||
})
|
||||
|
||||
const init = async (icon?: string) => {
|
||||
if (!icon) return
|
||||
@ -36,8 +30,8 @@ const iconSize = computed(() => {
|
||||
return unref(size) === 'small'
|
||||
? 'var(--el-component-size-small)'
|
||||
: unref(size) === 'large'
|
||||
? 'var(--el-component-size-large)'
|
||||
: 'var(--el-component-size)'
|
||||
? 'var(--el-component-size-large)'
|
||||
: 'var(--el-component-size)'
|
||||
})
|
||||
|
||||
const iconWrapStyle = computed((): CSSProperties => {
|
||||
@ -96,6 +90,11 @@ const popoverShow = () => {
|
||||
}
|
||||
|
||||
const iconSelect = (icon: string) => {
|
||||
// 如果是同一个icon则不做处理,则相当于点击了清空按钮
|
||||
if (icon === unref(modelValue)) {
|
||||
modelValue.value = ''
|
||||
return
|
||||
}
|
||||
modelValue.value = icon
|
||||
}
|
||||
|
||||
@ -112,7 +111,7 @@ const inputClear = () => {
|
||||
|
||||
<template>
|
||||
<div :class="prefixCls" class="flex justify-center items-center box">
|
||||
<ElInput :disabled="inputDisabled" v-model="modelValue" />
|
||||
<ElInput disabled v-model="modelValue" clearable />
|
||||
<ElPopover
|
||||
placement="bottom"
|
||||
trigger="click"
|
||||
@ -123,7 +122,6 @@ const inputClear = () => {
|
||||
<template #reference>
|
||||
<div :style="iconWrapStyle">
|
||||
<Icon v-if="modelValue" :icon="modelValue" />
|
||||
<Icon v-else icon="ep:setting" />
|
||||
</div>
|
||||
</template>
|
||||
<ElScrollbar class="h-[calc(100%-50px)]!">
|
||||
@ -151,8 +149,10 @@ const inputClear = () => {
|
||||
icon === modelValue ? 'var(--el-color-primary)' : 'var(--el-border-color)'
|
||||
}`,
|
||||
boxSizing: 'border-box',
|
||||
margin: '2px'
|
||||
margin: '2px',
|
||||
transition: 'all 0.3s'
|
||||
}"
|
||||
class="hover:border-color-[var(--el-color-primary)]!"
|
||||
@click="iconSelect(icon)"
|
||||
>
|
||||
<Icon
|
||||
|
3
kinit-admin/src/components/ImageCropping/index.ts
Normal file
3
kinit-admin/src/components/ImageCropping/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import ImageCropping from './src/ImageCropping.vue'
|
||||
|
||||
export { ImageCropping }
|
245
kinit-admin/src/components/ImageCropping/src/ImageCropping.vue
Normal file
245
kinit-admin/src/components/ImageCropping/src/ImageCropping.vue
Normal file
@ -0,0 +1,245 @@
|
||||
<script setup lang="ts">
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
import { nextTick, unref, ref, watch, onBeforeUnmount, onMounted, computed } from 'vue'
|
||||
import Cropper from 'cropperjs'
|
||||
import 'cropperjs/dist/cropper.min.css'
|
||||
import { ElDivider, ElUpload, UploadFile, ElMessage, ElTooltip } from 'element-plus'
|
||||
import { useDebounceFn } from '@vueuse/core'
|
||||
|
||||
const { getPrefixCls } = useDesign()
|
||||
const prefixCls = getPrefixCls('image-cropping')
|
||||
const props = defineProps({
|
||||
imageUrl: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: true
|
||||
},
|
||||
cropBoxWidth: {
|
||||
type: Number,
|
||||
default: 200
|
||||
},
|
||||
cropBoxHeight: {
|
||||
type: Number,
|
||||
default: 200
|
||||
},
|
||||
boxWidth: {
|
||||
type: [Number, String],
|
||||
default: 425
|
||||
},
|
||||
boxHeight: {
|
||||
type: [Number, String],
|
||||
default: 320
|
||||
},
|
||||
showResult: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
showActions: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
})
|
||||
const getBase64 = useDebounceFn(() => {
|
||||
imgBase64.value = unref(cropperRef)?.getCroppedCanvas()?.toDataURL() ?? ''
|
||||
}, 80)
|
||||
const resetCropBox = () => {
|
||||
const containerData = unref(cropperRef)?.getContainerData()
|
||||
unref(cropperRef)?.setCropBoxData({
|
||||
width: props.cropBoxWidth,
|
||||
height: props.cropBoxHeight,
|
||||
left: (containerData?.width || 0) / 2 - 100,
|
||||
top: (containerData?.height || 0) / 2 - 100
|
||||
})
|
||||
imgBase64.value = unref(cropperRef)?.getCroppedCanvas()?.toDataURL() ?? ''
|
||||
}
|
||||
const getBoxStyle = computed(() => {
|
||||
return {
|
||||
width: `${props.boxWidth}px`,
|
||||
height: `${props.boxHeight}px`
|
||||
}
|
||||
})
|
||||
const getCropBoxStyle = computed(() => {
|
||||
return {
|
||||
width: `${props.cropBoxWidth}px`,
|
||||
height: `${props.cropBoxHeight}px`
|
||||
}
|
||||
})
|
||||
|
||||
// 获取对应的缩小倍数的宽高
|
||||
const getScaleSize = (scale: number) => {
|
||||
return {
|
||||
width: props.cropBoxWidth * scale + 'px',
|
||||
height: props.cropBoxHeight * scale + 'px'
|
||||
}
|
||||
}
|
||||
|
||||
const imgBase64 = ref('')
|
||||
const imgRef = ref<HTMLImageElement>()
|
||||
const cropperRef = ref<Cropper>()
|
||||
const intiCropper = () => {
|
||||
if (!unref(imgRef)) return
|
||||
const imgEl = unref(imgRef)!
|
||||
cropperRef.value = new Cropper(imgEl, {
|
||||
aspectRatio: 1,
|
||||
viewMode: 1,
|
||||
dragMode: 'move',
|
||||
// cropBoxResizable: false,
|
||||
// cropBoxMovable: false,
|
||||
toggleDragModeOnDblclick: false,
|
||||
checkCrossOrigin: false,
|
||||
ready() {
|
||||
resetCropBox()
|
||||
},
|
||||
cropmove() {
|
||||
getBase64()
|
||||
},
|
||||
zoom() {
|
||||
getBase64()
|
||||
},
|
||||
crop() {
|
||||
getBase64()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const uploadChange = (uploadFile: UploadFile) => {
|
||||
// 判断是否是图片
|
||||
if (uploadFile?.raw?.type.indexOf('image') === -1) {
|
||||
ElMessage.error('请上传图片格式的文件')
|
||||
return
|
||||
}
|
||||
if (!uploadFile.raw) return
|
||||
// 获取图片的访问地址
|
||||
const url = URL.createObjectURL(uploadFile.raw)
|
||||
unref(cropperRef)?.replace(url)
|
||||
}
|
||||
const reset = () => {
|
||||
unref(cropperRef)?.reset()
|
||||
}
|
||||
const rotate = (deg: number) => {
|
||||
unref(cropperRef)?.rotate(deg)
|
||||
}
|
||||
const scaleX = ref(1)
|
||||
const scaleY = ref(1)
|
||||
const scale = (type: 'scaleX' | 'scaleY') => {
|
||||
if (type === 'scaleX') {
|
||||
scaleX.value = scaleX.value === 1 ? -1 : 1
|
||||
unref(cropperRef)?.[type](unref(scaleX))
|
||||
} else {
|
||||
scaleY.value = scaleY.value === 1 ? -1 : 1
|
||||
unref(cropperRef)?.[type](unref(scaleY))
|
||||
}
|
||||
}
|
||||
const zoom = (num: number) => {
|
||||
unref(cropperRef)?.zoom(num)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
intiCropper()
|
||||
})
|
||||
watch(
|
||||
() => props.imageUrl,
|
||||
async (url) => {
|
||||
if (url) {
|
||||
unref(cropperRef)?.replace(url)
|
||||
await nextTick()
|
||||
resetCropBox()
|
||||
}
|
||||
}
|
||||
)
|
||||
onBeforeUnmount(() => {
|
||||
unref(cropperRef)?.destroy()
|
||||
})
|
||||
defineExpose({
|
||||
cropperExpose: cropperRef
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:class="{
|
||||
[prefixCls]: true,
|
||||
'flex items-center': showResult
|
||||
}"
|
||||
>
|
||||
<div>
|
||||
<div :style="getBoxStyle" class="flex justify-center items-center">
|
||||
<img
|
||||
v-show="imageUrl"
|
||||
ref="imgRef"
|
||||
:src="imageUrl"
|
||||
class="block max-w-full"
|
||||
crossorigin="anonymous"
|
||||
alt=""
|
||||
srcset=""
|
||||
/>
|
||||
</div>
|
||||
<div v-if="showActions" class="mt-10px flex items-center">
|
||||
<div class="flex items-center">
|
||||
<ElTooltip content="选择文件" placement="bottom">
|
||||
<ElUpload
|
||||
action="''"
|
||||
accept="image/*"
|
||||
:auto-upload="false"
|
||||
:show-file-list="false"
|
||||
:on-change="uploadChange"
|
||||
>
|
||||
<BaseButton size="small" type="primary" class="mt-2px"
|
||||
><Icon icon="ep:upload-filled"
|
||||
/></BaseButton>
|
||||
</ElUpload>
|
||||
</ElTooltip>
|
||||
</div>
|
||||
<div class="flex items-center justify-end flex-1">
|
||||
<ElTooltip content="重置" placement="bottom">
|
||||
<BaseButton size="small" type="primary" @click="reset"
|
||||
><Icon icon="ep:refresh"
|
||||
/></BaseButton>
|
||||
</ElTooltip>
|
||||
<ElTooltip content="逆时针旋转" placement="bottom">
|
||||
<BaseButton size="small" type="primary" @click="rotate(-45)"
|
||||
><Icon icon="ant-design:rotate-left-outlined"
|
||||
/></BaseButton>
|
||||
</ElTooltip>
|
||||
<ElTooltip content="顺时针旋转" placement="bottom">
|
||||
<BaseButton size="small" type="primary" @click="rotate(45)"
|
||||
><Icon icon="ant-design:rotate-right-outlined"
|
||||
/></BaseButton>
|
||||
</ElTooltip>
|
||||
<ElTooltip content="水平翻转" placement="bottom">
|
||||
<BaseButton size="small" type="primary" @click="scale('scaleX')"
|
||||
><Icon icon="vaadin:arrows-long-h"
|
||||
/></BaseButton>
|
||||
</ElTooltip>
|
||||
<ElTooltip content="垂直翻转" placement="bottom">
|
||||
<BaseButton size="small" type="primary" @click="scale('scaleY')"
|
||||
><Icon icon="vaadin:arrows-long-v"
|
||||
/></BaseButton>
|
||||
</ElTooltip>
|
||||
<ElTooltip content="放大" placement="bottom">
|
||||
<BaseButton size="small" type="primary" @click="zoom(0.1)"
|
||||
><Icon icon="ant-design:zoom-in-outlined"
|
||||
/></BaseButton>
|
||||
</ElTooltip>
|
||||
<ElTooltip content="缩小" placement="bottom">
|
||||
<BaseButton size="small" type="primary" @click="zoom(-0.1)"
|
||||
><Icon icon="ant-design:zoom-out-outlined"
|
||||
/></BaseButton>
|
||||
</ElTooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="imgBase64 && showResult" class="ml-20px">
|
||||
<div class="flex justify-center items-center">
|
||||
<img :src="imgBase64" class="rounded-[50%]" :style="getCropBoxStyle" />
|
||||
</div>
|
||||
<ElDivider />
|
||||
<div class="flex justify-center items-center">
|
||||
<img :src="imgBase64" class="rounded-[50%]" :style="getScaleSize(0.2)" />
|
||||
<img :src="imgBase64" class="rounded-[50%] ml-20px" :style="getScaleSize(0.25)" />
|
||||
<img :src="imgBase64" class="rounded-[50%] ml-20px" :style="getScaleSize(0.3)" />
|
||||
<img :src="imgBase64" class="rounded-[50%] ml-20px" :style="getScaleSize(0.35)" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
@ -86,7 +86,7 @@ const getPasswordStrength = computed(() => {
|
||||
background-color: transparent;
|
||||
border-color: var(--el-color-white);
|
||||
border-style: solid;
|
||||
border-width: 0 5px 0 5px;
|
||||
border-width: 0 5px;
|
||||
content: '';
|
||||
}
|
||||
|
||||
|
@ -30,13 +30,7 @@ watch(
|
||||
show.value = true
|
||||
return
|
||||
}
|
||||
if (!collapse) {
|
||||
setTimeout(() => {
|
||||
show.value = !collapse
|
||||
}, 400)
|
||||
} else {
|
||||
show.value = !collapse
|
||||
}
|
||||
show.value = !collapse
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -123,30 +123,10 @@ export default defineComponent({
|
||||
<style lang="less" scoped>
|
||||
@prefix-cls: ~'@{namespace}-menu';
|
||||
|
||||
// .is-active--after {
|
||||
// position: absolute;
|
||||
// top: 0;
|
||||
// right: 0;
|
||||
// width: 4px;
|
||||
// height: 100%;
|
||||
// background-color: var(--el-color-primary);
|
||||
// content: '';
|
||||
// }
|
||||
|
||||
.@{prefix-cls} {
|
||||
position: relative;
|
||||
transition: width var(--transition-time-02);
|
||||
|
||||
// &:after {
|
||||
// position: absolute;
|
||||
// top: 0;
|
||||
// right: 0;
|
||||
// height: 100%;
|
||||
// width: 1px;
|
||||
// background-color: var(--el-border-color);
|
||||
// content: '';
|
||||
// }
|
||||
|
||||
:deep(.@{elNamespace}-menu) {
|
||||
width: 100% !important;
|
||||
border-right: none;
|
||||
@ -168,7 +148,6 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
// 设置选中时的高亮背景和高亮颜色
|
||||
.@{elNamespace}-sub-menu.is-active,
|
||||
.@{elNamespace}-menu-item.is-active {
|
||||
color: var(--left-menu-text-active-color) !important;
|
||||
background-color: var(--left-menu-bg-active-color) !important;
|
||||
@ -180,10 +159,6 @@ export default defineComponent({
|
||||
|
||||
.@{elNamespace}-menu-item.is-active {
|
||||
position: relative;
|
||||
|
||||
// &:after {
|
||||
// .is-active--after;
|
||||
// }
|
||||
}
|
||||
|
||||
// 设置子菜单的背景颜色
|
||||
@ -203,16 +178,11 @@ export default defineComponent({
|
||||
& > .is-active > .@{elNamespace}-sub-menu__title {
|
||||
position: relative;
|
||||
background-color: var(--left-menu-collapse-bg-active-color) !important;
|
||||
|
||||
// &:after {
|
||||
// .is-active--after;
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
// 折叠动画的时候,就需要把文字给隐藏掉
|
||||
:deep(.horizontal-collapse-transition) {
|
||||
// transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out !important;
|
||||
.@{prefix-cls}__title {
|
||||
display: none;
|
||||
}
|
||||
@ -235,7 +205,7 @@ export default defineComponent({
|
||||
.@{elNamespace}-menu-item.is-active {
|
||||
position: relative;
|
||||
|
||||
&:after {
|
||||
&::after {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
@ -254,16 +224,6 @@ export default defineComponent({
|
||||
<style lang="less">
|
||||
@prefix-cls: ~'@{namespace}-menu-popper';
|
||||
|
||||
// .is-active--after {
|
||||
// position: absolute;
|
||||
// top: 0;
|
||||
// right: 0;
|
||||
// width: 4px;
|
||||
// height: 100%;
|
||||
// background-color: var(--el-color-primary);
|
||||
// content: '';
|
||||
// }
|
||||
|
||||
.@{prefix-cls}--vertical,
|
||||
.@{prefix-cls}--horizontal {
|
||||
// 设置选中时子标题的颜色
|
||||
@ -290,10 +250,6 @@ export default defineComponent({
|
||||
&:hover {
|
||||
background-color: var(--left-menu-bg-active-color) !important;
|
||||
}
|
||||
|
||||
// &:after {
|
||||
// .is-active--after;
|
||||
// }
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -12,9 +12,10 @@ export const useRenderMenuItem = (
|
||||
menuMode: 'vertical' | 'horizontal'
|
||||
) => {
|
||||
const renderMenuItem = (routers: AppRouteRecordRaw[], parentPath = '/') => {
|
||||
return routers.map((v) => {
|
||||
const meta = v.meta ?? {}
|
||||
if (!meta.hidden) {
|
||||
return routers
|
||||
.filter((v) => !v.meta?.hidden)
|
||||
.map((v) => {
|
||||
const meta = v.meta ?? {}
|
||||
const { oneShowingChild, onlyOneChild } = hasOneShowingChild(v.children, v)
|
||||
const fullPath = isUrl(v.path) ? v.path : pathResolve(parentPath, v.path) // getAllParentPath<AppRouteRecordRaw>(allRouters, v.path).join('/')
|
||||
|
||||
@ -48,8 +49,7 @@ export const useRenderMenuItem = (
|
||||
</ElSubMenu>
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -10,10 +10,14 @@ export const useRenderMenuTitle = () => {
|
||||
return icon ? (
|
||||
<>
|
||||
<Icon icon={meta.icon}></Icon>
|
||||
<span class="v-menu__title">{t(title as string)}</span>
|
||||
<span class="v-menu__title overflow-hidden overflow-ellipsis whitespace-nowrap">
|
||||
{t(title as string)}
|
||||
</span>
|
||||
</>
|
||||
) : (
|
||||
<span class="v-menu__title">{t(title as string)}</span>
|
||||
<span class="v-menu__title overflow-hidden overflow-ellipsis whitespace-nowrap">
|
||||
{t(title as string)}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -242,7 +242,7 @@ const disabledClick = () => {
|
||||
|
||||
.@{prefix-cls} {
|
||||
&--disabled {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
background: rgb(255 255 255 / 95%);
|
||||
|
||||
& > div {
|
||||
transform: translate(-50%, -50%);
|
||||
|
@ -88,6 +88,9 @@ const newSchema = computed(() => {
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
label: () => {
|
||||
return <span> </span>
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -117,11 +120,14 @@ const setProps = (props: SearchProps = {}) => {
|
||||
outsideProps.value = props
|
||||
}
|
||||
|
||||
const schemaRef = ref<FormSchema[]>([])
|
||||
|
||||
// 监听表单结构化数组,重新生成formModel
|
||||
watch(
|
||||
() => unref(newSchema),
|
||||
async (schema = []) => {
|
||||
formModel.value = initModel(schema, unref(formModel))
|
||||
schemaRef.value = schema
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
@ -241,7 +247,7 @@ const onFormValidate = (prop: FormItemProp, isValid: boolean, message: string) =
|
||||
hide-required-asterisk
|
||||
:inline="getProps.inline"
|
||||
:is-col="getProps.isCol"
|
||||
:schema="newSchema"
|
||||
:schema="schemaRef"
|
||||
@register="formRegister"
|
||||
@validate="onFormValidate"
|
||||
/>
|
||||
|
@ -1,5 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
import { ElButton } from 'element-plus'
|
||||
import { useIcon } from '@/hooks/web/useIcon'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
@ -31,7 +30,7 @@ const onExpand = () => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElButton
|
||||
<BaseButton
|
||||
v-if="showSearch"
|
||||
type="primary"
|
||||
:loading="searchLoading"
|
||||
@ -39,21 +38,21 @@ const onExpand = () => {
|
||||
@click="onSearch"
|
||||
>
|
||||
{{ t('common.query') }}
|
||||
</ElButton>
|
||||
<ElButton
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
v-if="showReset"
|
||||
:loading="resetLoading"
|
||||
:icon="useIcon({ icon: 'ep:refresh-right' })"
|
||||
@click="onReset"
|
||||
>
|
||||
{{ t('common.reset') }}
|
||||
</ElButton>
|
||||
<ElButton
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
v-if="showExpand"
|
||||
:icon="useIcon({ icon: visible ? 'ep:arrow-down' : 'ep:arrow-up' })"
|
||||
:icon="useIcon({ icon: visible ? 'ep:arrow-up' : 'ep:arrow-down' })"
|
||||
text
|
||||
@click="onExpand"
|
||||
>
|
||||
{{ t(visible ? 'common.shrink' : 'common.expand') }}
|
||||
</ElButton>
|
||||
</BaseButton>
|
||||
</template>
|
||||
|
@ -1,12 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { ElDrawer, ElDivider, ElButton, ElMessage } from 'element-plus'
|
||||
import { ref, unref, computed, watch } from 'vue'
|
||||
import { ElDrawer, ElDivider, ElMessage } from 'element-plus'
|
||||
import { ref, unref } from 'vue'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { ThemeSwitch } from '@/components/ThemeSwitch'
|
||||
import { colorIsDark, lighten, hexToRGB } from '@/utils/color'
|
||||
import { useCssVar } from '@vueuse/core'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
import { trim, setCssVar } from '@/utils'
|
||||
import { trim, setCssVar, getCssVar } from '@/utils'
|
||||
import ColorRadioPicker from './components/ColorRadioPicker.vue'
|
||||
import InterfaceDisplay from './components/InterfaceDisplay.vue'
|
||||
import LayoutRadioPicker from './components/LayoutRadioPicker.vue'
|
||||
@ -14,7 +13,7 @@ import { useStorage } from '@/hooks/web/useStorage'
|
||||
import { useClipboard } from '@vueuse/core'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
|
||||
const { removeStorage } = useStorage()
|
||||
const { clear: storageClear } = useStorage('localStorage')
|
||||
|
||||
const { getPrefixCls } = useDesign()
|
||||
|
||||
@ -24,8 +23,6 @@ const appStore = useAppStore()
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const layout = computed(() => appStore.getLayout)
|
||||
|
||||
const drawer = ref(false)
|
||||
|
||||
// 主题色相关
|
||||
@ -42,70 +39,27 @@ const setSystemTheme = (color: string) => {
|
||||
const headerTheme = ref(appStore.getTheme.topHeaderBgColor || '')
|
||||
|
||||
const setHeaderTheme = (color: string) => {
|
||||
const isDarkColor = colorIsDark(color)
|
||||
const textColor = isDarkColor ? '#fff' : 'inherit'
|
||||
const textHoverColor = isDarkColor ? lighten(color!, 6) : '#f6f6f6'
|
||||
const topToolBorderColor = isDarkColor ? color : '#eee'
|
||||
setCssVar('--top-header-bg-color', color)
|
||||
setCssVar('--top-header-text-color', textColor)
|
||||
setCssVar('--top-header-hover-color', textHoverColor)
|
||||
appStore.setTheme({
|
||||
topHeaderBgColor: color,
|
||||
topHeaderTextColor: textColor,
|
||||
topHeaderHoverColor: textHoverColor,
|
||||
topToolBorderColor
|
||||
})
|
||||
if (unref(layout) === 'top') {
|
||||
setMenuTheme(color)
|
||||
}
|
||||
appStore.setHeaderTheme(color)
|
||||
}
|
||||
|
||||
// 菜单主题相关
|
||||
const menuTheme = ref(appStore.getTheme.leftMenuBgColor || '')
|
||||
|
||||
const setMenuTheme = (color: string) => {
|
||||
const primaryColor = useCssVar('--el-color-primary', document.documentElement)
|
||||
const isDarkColor = colorIsDark(color)
|
||||
const theme: Recordable = {
|
||||
// 左侧菜单边框颜色
|
||||
leftMenuBorderColor: isDarkColor ? 'inherit' : '#eee',
|
||||
// 左侧菜单背景颜色
|
||||
leftMenuBgColor: color,
|
||||
// 左侧菜单浅色背景颜色
|
||||
leftMenuBgLightColor: isDarkColor ? lighten(color!, 6) : color,
|
||||
// 左侧菜单选中背景颜色
|
||||
leftMenuBgActiveColor: isDarkColor
|
||||
? 'var(--el-color-primary)'
|
||||
: hexToRGB(unref(primaryColor), 0.1),
|
||||
// 左侧菜单收起选中背景颜色
|
||||
leftMenuCollapseBgActiveColor: isDarkColor
|
||||
? 'var(--el-color-primary)'
|
||||
: hexToRGB(unref(primaryColor), 0.1),
|
||||
// 左侧菜单字体颜色
|
||||
leftMenuTextColor: isDarkColor ? '#bfcbd9' : '#333',
|
||||
// 左侧菜单选中字体颜色
|
||||
leftMenuTextActiveColor: isDarkColor ? '#fff' : 'var(--el-color-primary)',
|
||||
// logo字体颜色
|
||||
logoTitleTextColor: isDarkColor ? '#fff' : 'inherit',
|
||||
// logo边框颜色
|
||||
logoBorderColor: isDarkColor ? color : '#eee'
|
||||
}
|
||||
appStore.setTheme(theme)
|
||||
appStore.setCssVarTheme()
|
||||
appStore.setMenuTheme(color)
|
||||
}
|
||||
|
||||
// 监听layout变化,重置一些主题色
|
||||
watch(
|
||||
() => layout.value,
|
||||
(n) => {
|
||||
if (n === 'top' && !appStore.getIsDark) {
|
||||
headerTheme.value = '#fff'
|
||||
setHeaderTheme('#fff')
|
||||
} else {
|
||||
setMenuTheme(unref(menuTheme))
|
||||
}
|
||||
}
|
||||
)
|
||||
// watch(
|
||||
// () => layout.value,
|
||||
// (n) => {
|
||||
// if (n === 'top' && !appStore.getIsDark) {
|
||||
// headerTheme.value = '#fff'
|
||||
// setHeaderTheme('#fff')
|
||||
// } else {
|
||||
// setMenuTheme(unref(menuTheme))
|
||||
// }
|
||||
// }
|
||||
// )
|
||||
|
||||
// 拷贝
|
||||
const copyConfig = async () => {
|
||||
@ -174,7 +128,8 @@ const copyConfig = async () => {
|
||||
// 头部边框颜色
|
||||
topToolBorderColor: '${appStore.getTheme.topToolBorderColor}'
|
||||
}
|
||||
`
|
||||
`,
|
||||
legacy: true
|
||||
})
|
||||
if (!isSupported) {
|
||||
ElMessage.error(t('setting.copyFailed'))
|
||||
@ -188,11 +143,15 @@ const copyConfig = async () => {
|
||||
|
||||
// 清空缓存
|
||||
const clear = () => {
|
||||
removeStorage('layout')
|
||||
removeStorage('theme')
|
||||
removeStorage('isDark')
|
||||
storageClear()
|
||||
window.location.reload()
|
||||
}
|
||||
|
||||
const themeChange = () => {
|
||||
const color = getCssVar('--el-bg-color')
|
||||
setMenuTheme(color)
|
||||
setHeaderTheme(color)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -212,7 +171,7 @@ const clear = () => {
|
||||
<div class="text-center">
|
||||
<!-- 主题 -->
|
||||
<ElDivider>{{ t('setting.theme') }}</ElDivider>
|
||||
<ThemeSwitch />
|
||||
<ThemeSwitch @change="themeChange" />
|
||||
|
||||
<!-- 布局 -->
|
||||
<ElDivider>{{ t('setting.layout') }}</ElDivider>
|
||||
@ -253,23 +212,21 @@ const clear = () => {
|
||||
/>
|
||||
|
||||
<!-- 菜单主题 -->
|
||||
<template v-if="layout !== 'top'">
|
||||
<ElDivider>{{ t('setting.menuTheme') }}</ElDivider>
|
||||
<ColorRadioPicker
|
||||
v-model="menuTheme"
|
||||
:schema="[
|
||||
'#fff',
|
||||
'#001529',
|
||||
'#212121',
|
||||
'#273352',
|
||||
'#191b24',
|
||||
'#383f45',
|
||||
'#001628',
|
||||
'#344058'
|
||||
]"
|
||||
@change="setMenuTheme"
|
||||
/>
|
||||
</template>
|
||||
<ElDivider>{{ t('setting.menuTheme') }}</ElDivider>
|
||||
<ColorRadioPicker
|
||||
v-model="menuTheme"
|
||||
:schema="[
|
||||
'#fff',
|
||||
'#001529',
|
||||
'#212121',
|
||||
'#273352',
|
||||
'#191b24',
|
||||
'#383f45',
|
||||
'#001628',
|
||||
'#344058'
|
||||
]"
|
||||
@change="setMenuTheme"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 界面显示 -->
|
||||
@ -278,12 +235,14 @@ const clear = () => {
|
||||
|
||||
<ElDivider />
|
||||
<div>
|
||||
<ElButton type="primary" class="w-full" @click="copyConfig">{{ t('setting.copy') }}</ElButton>
|
||||
<BaseButton type="primary" class="w-full" @click="copyConfig">{{
|
||||
t('setting.copy')
|
||||
}}</BaseButton>
|
||||
</div>
|
||||
<div class="mt-5px">
|
||||
<ElButton type="danger" class="w-full" @click="clear">
|
||||
<BaseButton type="danger" class="w-full" @click="clear">
|
||||
{{ t('setting.clearAndReset') }}
|
||||
</ElButton>
|
||||
</BaseButton>
|
||||
</div>
|
||||
</ElDrawer>
|
||||
</template>
|
||||
|
@ -67,7 +67,7 @@ const layout = computed(() => appStore.getLayout)
|
||||
border: 2px solid #e5e7eb;
|
||||
border-radius: 4px;
|
||||
|
||||
&:before {
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
@ -79,7 +79,7 @@ const layout = computed(() => appStore.getLayout)
|
||||
content: '';
|
||||
}
|
||||
|
||||
&:after {
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
@ -95,7 +95,7 @@ const layout = computed(() => appStore.getLayout)
|
||||
border: 2px solid #e5e7eb;
|
||||
border-radius: 4px;
|
||||
|
||||
&:before {
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
@ -107,7 +107,7 @@ const layout = computed(() => appStore.getLayout)
|
||||
content: '';
|
||||
}
|
||||
|
||||
&:after {
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
@ -123,7 +123,7 @@ const layout = computed(() => appStore.getLayout)
|
||||
border: 2px solid #e5e7eb;
|
||||
border-radius: 4px;
|
||||
|
||||
&:before {
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
@ -140,7 +140,7 @@ const layout = computed(() => appStore.getLayout)
|
||||
border: 2px solid #e5e7eb;
|
||||
border-radius: 4px;
|
||||
|
||||
&:before {
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
@ -152,7 +152,7 @@ const layout = computed(() => appStore.getLayout)
|
||||
content: '';
|
||||
}
|
||||
|
||||
&:after {
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
@ -1,12 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { ElDrawer, ElDivider, ElButton, ElMessage } from 'element-plus'
|
||||
import { ref, unref, computed, watch } from 'vue'
|
||||
import { ElDrawer, ElDivider, ElMessage } from 'element-plus'
|
||||
import { ref, unref } from 'vue'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { ThemeSwitch } from '@/components/ThemeSwitch'
|
||||
import { colorIsDark, lighten, hexToRGB } from '@/utils/color'
|
||||
import { useCssVar } from '@vueuse/core'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
import { trim, setCssVar } from '@/utils'
|
||||
import { trim, setCssVar, getCssVar } from '@/utils'
|
||||
import ColorRadioPicker from './components/ColorRadioPicker.vue'
|
||||
import InterfaceDisplay from './components/InterfaceDisplay.vue'
|
||||
import LayoutRadioPicker from './components/LayoutRadioPicker.vue'
|
||||
@ -15,7 +14,7 @@ import { useClipboard } from '@vueuse/core'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
|
||||
const { removeStorage } = useStorage()
|
||||
const { clear: storageClear } = useStorage('localStorage')
|
||||
|
||||
const { getPrefixCls } = useDesign()
|
||||
|
||||
@ -29,8 +28,6 @@ const appStore = useAppStore()
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const layout = computed(() => appStore.getLayout)
|
||||
|
||||
const drawer = ref(false)
|
||||
|
||||
// 主题色相关
|
||||
@ -47,70 +44,27 @@ const setSystemTheme = (color: string) => {
|
||||
const headerTheme = ref(appStore.getTheme.topHeaderBgColor || '')
|
||||
|
||||
const setHeaderTheme = (color: string) => {
|
||||
const isDarkColor = colorIsDark(color)
|
||||
const textColor = isDarkColor ? '#fff' : 'inherit'
|
||||
const textHoverColor = isDarkColor ? lighten(color!, 6) : '#f6f6f6'
|
||||
const topToolBorderColor = isDarkColor ? color : '#eee'
|
||||
setCssVar('--top-header-bg-color', color)
|
||||
setCssVar('--top-header-text-color', textColor)
|
||||
setCssVar('--top-header-hover-color', textHoverColor)
|
||||
appStore.setTheme({
|
||||
topHeaderBgColor: color,
|
||||
topHeaderTextColor: textColor,
|
||||
topHeaderHoverColor: textHoverColor,
|
||||
topToolBorderColor
|
||||
})
|
||||
if (unref(layout) === 'top') {
|
||||
setMenuTheme(color)
|
||||
}
|
||||
appStore.setHeaderTheme(color)
|
||||
}
|
||||
|
||||
// 菜单主题相关
|
||||
const menuTheme = ref(appStore.getTheme.leftMenuBgColor || '')
|
||||
|
||||
const setMenuTheme = (color: string) => {
|
||||
const primaryColor = useCssVar('--el-color-primary', document.documentElement)
|
||||
const isDarkColor = colorIsDark(color)
|
||||
const theme: Recordable = {
|
||||
// 左侧菜单边框颜色
|
||||
leftMenuBorderColor: isDarkColor ? 'inherit' : '#eee',
|
||||
// 左侧菜单背景颜色
|
||||
leftMenuBgColor: color,
|
||||
// 左侧菜单浅色背景颜色
|
||||
leftMenuBgLightColor: isDarkColor ? lighten(color!, 6) : color,
|
||||
// 左侧菜单选中背景颜色
|
||||
leftMenuBgActiveColor: isDarkColor
|
||||
? 'var(--el-color-primary)'
|
||||
: hexToRGB(unref(primaryColor), 0.1),
|
||||
// 左侧菜单收起选中背景颜色
|
||||
leftMenuCollapseBgActiveColor: isDarkColor
|
||||
? 'var(--el-color-primary)'
|
||||
: hexToRGB(unref(primaryColor), 0.1),
|
||||
// 左侧菜单字体颜色
|
||||
leftMenuTextColor: isDarkColor ? '#bfcbd9' : '#333',
|
||||
// 左侧菜单选中字体颜色
|
||||
leftMenuTextActiveColor: isDarkColor ? '#fff' : 'var(--el-color-primary)',
|
||||
// logo字体颜色
|
||||
logoTitleTextColor: isDarkColor ? '#fff' : 'inherit',
|
||||
// logo边框颜色
|
||||
logoBorderColor: isDarkColor ? color : '#eee'
|
||||
}
|
||||
appStore.setTheme(theme)
|
||||
appStore.setCssVarTheme()
|
||||
appStore.setMenuTheme(color)
|
||||
}
|
||||
|
||||
// 监听layout变化,重置一些主题色
|
||||
watch(
|
||||
() => layout.value,
|
||||
(n) => {
|
||||
if (n === 'top' && !appStore.getIsDark) {
|
||||
headerTheme.value = '#fff'
|
||||
setHeaderTheme('#fff')
|
||||
} else {
|
||||
setMenuTheme(unref(menuTheme))
|
||||
}
|
||||
}
|
||||
)
|
||||
// watch(
|
||||
// () => layout.value,
|
||||
// (n) => {
|
||||
// if (n === 'top' && !appStore.getIsDark) {
|
||||
// headerTheme.value = '#fff'
|
||||
// setHeaderTheme('#fff')
|
||||
// } else {
|
||||
// setMenuTheme(unref(menuTheme))
|
||||
// }
|
||||
// }
|
||||
// )
|
||||
|
||||
// 拷贝
|
||||
const copyConfig = async () => {
|
||||
@ -179,7 +133,8 @@ const copyConfig = async () => {
|
||||
// 头部边框颜色
|
||||
topToolBorderColor: '${appStore.getTheme.topToolBorderColor}'
|
||||
}
|
||||
`
|
||||
`,
|
||||
legacy: true
|
||||
})
|
||||
if (!isSupported) {
|
||||
ElMessage.error(t('setting.copyFailed'))
|
||||
@ -193,11 +148,15 @@ const copyConfig = async () => {
|
||||
|
||||
// 清空缓存
|
||||
const clear = () => {
|
||||
removeStorage('layout')
|
||||
removeStorage('theme')
|
||||
removeStorage('isDark')
|
||||
storageClear()
|
||||
window.location.reload()
|
||||
}
|
||||
|
||||
const themeChange = () => {
|
||||
const color = getCssVar('--el-bg-color')
|
||||
setMenuTheme(color)
|
||||
setHeaderTheme(color)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -219,7 +178,7 @@ const clear = () => {
|
||||
<div class="text-center">
|
||||
<!-- 主题 -->
|
||||
<ElDivider>{{ t('setting.theme') }}</ElDivider>
|
||||
<ThemeSwitch />
|
||||
<ThemeSwitch @change="themeChange" />
|
||||
|
||||
<!-- 布局 -->
|
||||
<ElDivider>{{ t('setting.layout') }}</ElDivider>
|
||||
@ -260,23 +219,21 @@ const clear = () => {
|
||||
/>
|
||||
|
||||
<!-- 菜单主题 -->
|
||||
<template v-if="layout !== 'top'">
|
||||
<ElDivider>{{ t('setting.menuTheme') }}</ElDivider>
|
||||
<ColorRadioPicker
|
||||
v-model="menuTheme"
|
||||
:schema="[
|
||||
'#fff',
|
||||
'#001529',
|
||||
'#212121',
|
||||
'#273352',
|
||||
'#191b24',
|
||||
'#383f45',
|
||||
'#001628',
|
||||
'#344058'
|
||||
]"
|
||||
@change="setMenuTheme"
|
||||
/>
|
||||
</template>
|
||||
<ElDivider>{{ t('setting.menuTheme') }}</ElDivider>
|
||||
<ColorRadioPicker
|
||||
v-model="menuTheme"
|
||||
:schema="[
|
||||
'#fff',
|
||||
'#001529',
|
||||
'#212121',
|
||||
'#273352',
|
||||
'#191b24',
|
||||
'#383f45',
|
||||
'#001628',
|
||||
'#344058'
|
||||
]"
|
||||
@change="setMenuTheme"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 界面显示 -->
|
||||
@ -285,14 +242,14 @@ const clear = () => {
|
||||
|
||||
<ElDivider />
|
||||
<div>
|
||||
<ElButton type="primary" class="w-full" @click="copyConfig">{{
|
||||
<BaseButton type="primary" class="w-full" @click="copyConfig">{{
|
||||
t('setting.copy')
|
||||
}}</ElButton>
|
||||
}}</BaseButton>
|
||||
</div>
|
||||
<div class="mt-5px">
|
||||
<ElButton type="danger" class="w-full" @click="clear">
|
||||
<BaseButton type="danger" class="w-full" @click="clear">
|
||||
{{ t('setting.clearAndReset') }}
|
||||
</ElButton>
|
||||
</BaseButton>
|
||||
</div>
|
||||
</ElDrawer>
|
||||
</div>
|
||||
|
@ -67,7 +67,7 @@ const layout = computed(() => appStore.getLayout)
|
||||
border: 2px solid #e5e7eb;
|
||||
border-radius: 4px;
|
||||
|
||||
&:before {
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
@ -79,7 +79,7 @@ const layout = computed(() => appStore.getLayout)
|
||||
content: '';
|
||||
}
|
||||
|
||||
&:after {
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
@ -95,7 +95,7 @@ const layout = computed(() => appStore.getLayout)
|
||||
border: 2px solid #e5e7eb;
|
||||
border-radius: 4px;
|
||||
|
||||
&:before {
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
@ -107,7 +107,7 @@ const layout = computed(() => appStore.getLayout)
|
||||
content: '';
|
||||
}
|
||||
|
||||
&:after {
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
@ -123,7 +123,7 @@ const layout = computed(() => appStore.getLayout)
|
||||
border: 2px solid #e5e7eb;
|
||||
border-radius: 4px;
|
||||
|
||||
&:before {
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
@ -140,7 +140,7 @@ const layout = computed(() => appStore.getLayout)
|
||||
border: 2px solid #e5e7eb;
|
||||
border-radius: 4px;
|
||||
|
||||
&:before {
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
@ -152,7 +152,7 @@ const layout = computed(() => appStore.getLayout)
|
||||
content: '';
|
||||
}
|
||||
|
||||
&:after {
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
@ -5,7 +5,9 @@ import {
|
||||
ElPagination,
|
||||
ComponentSize,
|
||||
ElTooltipProps,
|
||||
ElImage
|
||||
ElImage,
|
||||
ElEmpty,
|
||||
ElCard
|
||||
} from 'element-plus'
|
||||
import { defineComponent, PropType, ref, computed, unref, watch, onMounted } from 'vue'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
@ -16,6 +18,9 @@ import { CSSProperties } from 'vue'
|
||||
import { getSlot } from '@/utils/tsxHelper'
|
||||
import TableActions from './components/TableActions.vue'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
import { createVideoViewer } from '@/components/VideoPlayer'
|
||||
import { Icon } from '@/components/Icon'
|
||||
import { BaseButton } from '@/components/Button'
|
||||
|
||||
const appStore = useAppStore()
|
||||
|
||||
@ -58,8 +63,13 @@ export default defineComponent({
|
||||
type: Array as PropType<Recordable[]>,
|
||||
default: () => []
|
||||
},
|
||||
// 是否自动预览
|
||||
preview: {
|
||||
// 图片自动预览字段数组
|
||||
imagePreview: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: () => []
|
||||
},
|
||||
// 视频自动预览字段数组
|
||||
videoPreview: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: () => []
|
||||
},
|
||||
@ -189,7 +199,25 @@ export default defineComponent({
|
||||
default: 'fixed'
|
||||
},
|
||||
scrollbarAlwaysOn: propTypes.bool.def(false),
|
||||
flexible: propTypes.bool.def(false)
|
||||
flexible: propTypes.bool.def(false),
|
||||
// 自定义内容
|
||||
customContent: propTypes.bool.def(false),
|
||||
cardBodyStyle: {
|
||||
type: Object as PropType<CSSProperties>,
|
||||
default: () => ({})
|
||||
},
|
||||
cardBodyClass: {
|
||||
type: String as PropType<string>,
|
||||
default: ''
|
||||
},
|
||||
cardWrapStyle: {
|
||||
type: Object as PropType<CSSProperties>,
|
||||
default: () => ({})
|
||||
},
|
||||
cardWrapClass: {
|
||||
type: String as PropType<string>,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
emits: ['update:pageSize', 'update:currentPage', 'register', 'refresh'],
|
||||
setup(props, { attrs, emit, slots, expose }) {
|
||||
@ -236,7 +264,7 @@ export default defineComponent({
|
||||
|
||||
const addColumn = (column: TableColumn, index?: number) => {
|
||||
const { columns } = unref(getProps)
|
||||
if (index) {
|
||||
if (index !== void 0) {
|
||||
columns.splice(index, 0, column)
|
||||
} else {
|
||||
columns.push(column)
|
||||
@ -325,7 +353,8 @@ export default defineComponent({
|
||||
})
|
||||
|
||||
const renderTreeTableColumn = (columnsChildren: TableColumn[]) => {
|
||||
const { align, headerAlign, showOverflowTooltip, preview } = unref(getProps)
|
||||
const { align, headerAlign, showOverflowTooltip, imagePreview, videoPreview } =
|
||||
unref(getProps)
|
||||
return columnsChildren.map((v) => {
|
||||
if (v.show === false) return null
|
||||
const props = { ...v } as any
|
||||
@ -336,20 +365,20 @@ export default defineComponent({
|
||||
const slots = {
|
||||
default: (...args: any[]) => {
|
||||
const data = args[0]
|
||||
let isImageUrl = false
|
||||
if (preview.length) {
|
||||
isImageUrl = preview.some((item) => (item as string) === v.field)
|
||||
}
|
||||
let isPreview = false
|
||||
isPreview =
|
||||
imagePreview.some((item) => (item as string) === v.field) ||
|
||||
videoPreview.some((item) => (item as string) === v.field)
|
||||
|
||||
return children && children.length
|
||||
? renderTreeTableColumn(children)
|
||||
: props?.slots?.default
|
||||
? props.slots.default(...args)
|
||||
: v?.formatter
|
||||
? v?.formatter?.(data.row, data.column, get(data.row, v.field), data.$index)
|
||||
: isImageUrl
|
||||
? renderPreview(get(data.row, v.field))
|
||||
: get(data.row, v.field)
|
||||
? props.slots.default(...args)
|
||||
: v?.formatter
|
||||
? v?.formatter?.(data.row, data.column, get(data.row, v.field), data.$index)
|
||||
: isPreview
|
||||
? renderPreview(get(data.row, v.field), v.field)
|
||||
: get(data.row, v.field)
|
||||
}
|
||||
}
|
||||
if (props?.slots?.header) {
|
||||
@ -370,17 +399,32 @@ export default defineComponent({
|
||||
})
|
||||
}
|
||||
|
||||
const renderPreview = (url: string) => {
|
||||
const renderPreview = (url: string, field: string) => {
|
||||
const { imagePreview, videoPreview } = unref(getProps)
|
||||
return (
|
||||
<div class="flex items-center">
|
||||
<ElImage
|
||||
src={url}
|
||||
fit="cover"
|
||||
class="w-[100%] h-100px"
|
||||
lazy
|
||||
preview-src-list={[url]}
|
||||
preview-teleported
|
||||
/>
|
||||
{imagePreview.includes(field) ? (
|
||||
<ElImage
|
||||
src={url}
|
||||
fit="cover"
|
||||
class="w-[100%]"
|
||||
lazy
|
||||
preview-src-list={[url]}
|
||||
preview-teleported
|
||||
/>
|
||||
) : videoPreview.includes(field) ? (
|
||||
<BaseButton
|
||||
type="primary"
|
||||
icon={<Icon icon="ep:video-play" />}
|
||||
onClick={() => {
|
||||
createVideoViewer({
|
||||
url
|
||||
})
|
||||
}}
|
||||
>
|
||||
预览
|
||||
</BaseButton>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -395,7 +439,8 @@ export default defineComponent({
|
||||
headerAlign,
|
||||
showOverflowTooltip,
|
||||
reserveSelection,
|
||||
preview
|
||||
imagePreview,
|
||||
videoPreview
|
||||
} = unref(getProps)
|
||||
|
||||
return (columnsChildren || columns).map((v) => {
|
||||
@ -421,6 +466,7 @@ export default defineComponent({
|
||||
reserveSelection={reserveSelection}
|
||||
align="center"
|
||||
headerAlign="center"
|
||||
selectable={v.selectable}
|
||||
width="50px"
|
||||
fixed="left"
|
||||
></ElTableColumn>
|
||||
@ -435,20 +481,20 @@ export default defineComponent({
|
||||
default: (...args: any[]) => {
|
||||
const data = args[0]
|
||||
|
||||
let isImageUrl = false
|
||||
if (preview.length) {
|
||||
isImageUrl = preview.some((item) => (item as string) === v.field)
|
||||
}
|
||||
let isPreview = false
|
||||
isPreview =
|
||||
imagePreview.some((item) => (item as string) === v.field) ||
|
||||
videoPreview.some((item) => (item as string) === v.field)
|
||||
|
||||
return children && children.length
|
||||
? renderTreeTableColumn(children)
|
||||
: props?.slots?.default
|
||||
? props.slots.default(...args)
|
||||
: v?.formatter
|
||||
? v?.formatter?.(data.row, data.column, get(data.row, v.field), data.$index)
|
||||
: isImageUrl
|
||||
? renderPreview(get(data.row, v.field))
|
||||
: get(data.row, v.field)
|
||||
? props.slots.default(...args)
|
||||
: v?.formatter
|
||||
? v?.formatter?.(data.row, data.column, get(data.row, v.field), data.$index)
|
||||
: isPreview
|
||||
? renderPreview(get(data.row, v.field), v.field)
|
||||
: get(data.row, v.field)
|
||||
}
|
||||
}
|
||||
if (props?.slots?.header) {
|
||||
@ -482,36 +528,77 @@ export default defineComponent({
|
||||
|
||||
return (
|
||||
<div v-loading={unref(getProps).loading}>
|
||||
<div class="flex justify-between mb-1">
|
||||
<div>{toolbar}</div>
|
||||
<div class="pt-2">
|
||||
{unref(getProps).showAction ? (
|
||||
<TableActions
|
||||
activeUID={unref(getProps).activeUID}
|
||||
columns={unref(getProps).columns}
|
||||
el-table-ref={elTableRef}
|
||||
onChangSize={changSize}
|
||||
onRefresh={refresh}
|
||||
/>
|
||||
) : null}
|
||||
{unref(getProps).customContent ? (
|
||||
<div class="flex flex-wrap">
|
||||
{unref(getProps)?.data?.length ? (
|
||||
unref(getProps)?.data.map((item) => {
|
||||
const cardSlots = {
|
||||
default: () => {
|
||||
return getSlot(slots, 'content', item)
|
||||
}
|
||||
}
|
||||
if (getSlot(slots, 'content-header')) {
|
||||
cardSlots['header'] = () => {
|
||||
return getSlot(slots, 'content-header', item)
|
||||
}
|
||||
}
|
||||
if (getSlot(slots, 'content-footer')) {
|
||||
cardSlots['footer'] = () => {
|
||||
return getSlot(slots, 'content-footer', item)
|
||||
}
|
||||
}
|
||||
return (
|
||||
<ElCard
|
||||
shadow="hover"
|
||||
class={unref(getProps).cardWrapClass}
|
||||
style={unref(getProps).cardWrapStyle}
|
||||
bodyClass={unref(getProps).cardBodyClass}
|
||||
bodyStyle={unref(getProps).cardBodyStyle}
|
||||
>
|
||||
{cardSlots}
|
||||
</ElCard>
|
||||
)
|
||||
})
|
||||
) : (
|
||||
<div class="flex flex-1 justify-center">
|
||||
<ElEmpty description="暂无数据" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div class="flex justify-between mb-1">
|
||||
<div>{toolbar}</div>
|
||||
<div class="pt-2">
|
||||
{unref(getProps).showAction ? (
|
||||
<TableActions
|
||||
activeUID={unref(getProps).activeUID}
|
||||
columns={unref(getProps).columns}
|
||||
el-table-ref={elTableRef}
|
||||
onChangSize={changSize}
|
||||
onRefresh={refresh}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
<ElTable
|
||||
ref={elTableRef}
|
||||
data={unref(getProps).data}
|
||||
{...unref(getBindValue)}
|
||||
header-cell-style={
|
||||
appStore.getIsDark
|
||||
? { color: '#CFD3DC', 'background-color': '#000' }
|
||||
: { color: '#000', 'background-color': '#f5f7fa' }
|
||||
}
|
||||
>
|
||||
{{
|
||||
default: () => renderTableColumn(),
|
||||
...tableSlots
|
||||
}}
|
||||
</ElTable>
|
||||
</>
|
||||
)}
|
||||
|
||||
<ElTable
|
||||
ref={elTableRef}
|
||||
data={unref(getProps).data}
|
||||
{...unref(getBindValue)}
|
||||
header-cell-style={
|
||||
appStore.getIsDark
|
||||
? { color: '#CFD3DC', 'background-color': '#000' }
|
||||
: { color: '#000', 'background-color': '#f5f7fa' }
|
||||
}
|
||||
>
|
||||
{{
|
||||
default: () => renderTableColumn(),
|
||||
...tableSlots
|
||||
}}
|
||||
</ElTable>
|
||||
{unref(getProps).pagination ? (
|
||||
<ElPagination
|
||||
v-model:pageSize={pageSizeRef.value}
|
||||
|
@ -9,7 +9,6 @@ import {
|
||||
ElPopover,
|
||||
ElCheckbox,
|
||||
ElScrollbar,
|
||||
ElButton,
|
||||
ElTable,
|
||||
ElDivider
|
||||
} from 'element-plus'
|
||||
@ -23,6 +22,7 @@ import { useStorage } from '@/hooks/web/useStorage'
|
||||
import cloneDeep from 'lodash/cloneDeep'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { moveElementToIndex } from '@/utils/index'
|
||||
import { BaseButton } from '@/components/Button'
|
||||
|
||||
const appStore = useAppStore()
|
||||
const sizeMap = computed(() => appStore.sizeMap)
|
||||
@ -263,9 +263,9 @@ export default defineComponent({
|
||||
{t('common.SerialNumberColumn')}
|
||||
</ElCheckbox>
|
||||
</div>
|
||||
<ElButton type="primary" link onClick={resetTableColumns}>
|
||||
<BaseButton type="primary" link onClick={resetTableColumns}>
|
||||
{t('common.reset')}
|
||||
</ElButton>
|
||||
</BaseButton>
|
||||
</div>
|
||||
<ElScrollbar max-height="400px">
|
||||
<VueDraggable
|
||||
|
@ -9,6 +9,8 @@ const { getPrefixCls } = useDesign()
|
||||
|
||||
const prefixCls = getPrefixCls('theme-switch')
|
||||
|
||||
const emit = defineEmits(['change'])
|
||||
|
||||
const Sun = useIcon({ icon: 'emojione-monotone:sun', color: '#fde047' })
|
||||
|
||||
const CrescentMoon = useIcon({ icon: 'emojione-monotone:crescent-moon', color: '#fde047' })
|
||||
@ -23,6 +25,7 @@ const blackColor = 'var(--el-color-black)'
|
||||
|
||||
const themeChange = (val: boolean) => {
|
||||
appStore.setIsDark(val)
|
||||
emit('change', val)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ElDropdown, ElDropdownMenu, ElDropdownItem, ElMessageBox } from 'element-plus'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { useAuthStoreWithOut } from '@/store/modules/auth'
|
||||
import { useAuthStore } from '@/store/modules/auth'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
import LockDialog from './components/LockDialog.vue'
|
||||
import { ref, computed } from 'vue'
|
||||
@ -14,7 +14,7 @@ const lockStore = useLockStore()
|
||||
|
||||
const getIsLock = computed(() => lockStore.getLockInfo?.isLock ?? false)
|
||||
|
||||
const authStore = useAuthStoreWithOut()
|
||||
const authStore = useAuthStore()
|
||||
|
||||
const { getPrefixCls } = useDesign()
|
||||
|
||||
@ -73,13 +73,13 @@ const user = computed(() => authStore.getUser)
|
||||
<template #dropdown>
|
||||
<ElDropdownMenu>
|
||||
<ElDropdownItem>
|
||||
<ElButton @click="toHome" link>个人主页</ElButton>
|
||||
<BaseButton @click="toHome" link>个人主页</BaseButton>
|
||||
</ElDropdownItem>
|
||||
<ElDropdownItem>
|
||||
<ElButton @click="toGitee" link>Gitee</ElButton>
|
||||
<BaseButton @click="toGitee" link>Gitee</BaseButton>
|
||||
</ElDropdownItem>
|
||||
<ElDropdownItem>
|
||||
<ElButton @click="toGithub" link>Github</ElButton>
|
||||
<BaseButton @click="toGithub" link>Github</BaseButton>
|
||||
</ElDropdownItem>
|
||||
<ElDropdownItem divided>
|
||||
<div @click="lockScreen">{{ t('lock.lockScreen') }}</div>
|
||||
|
@ -7,7 +7,6 @@ import { useForm } from '@/hooks/web/useForm'
|
||||
import { reactive, computed } from 'vue'
|
||||
import { useValidator } from '@/hooks/web/useValidator'
|
||||
import { FormSchema } from '@/components/Form'
|
||||
import { ElButton } from 'element-plus'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
import { useLockStore } from '@/store/modules/lock'
|
||||
|
||||
@ -87,14 +86,14 @@ const handleLock = async () => {
|
||||
</div>
|
||||
<Form :is-col="false" :schema="schema" :rules="rules" @register="formRegister" />
|
||||
<template #footer>
|
||||
<ElButton type="primary" @click="handleLock">{{ t('lock.lock') }}</ElButton>
|
||||
<BaseButton type="primary" @click="handleLock">{{ t('lock.lock') }}</BaseButton>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
:global(.v-lock-dialog) {
|
||||
@media (max-width: 767px) {
|
||||
@media (width <= 767px) {
|
||||
max-width: calc(100vw - 16px);
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import { ElInput, ElButton } from 'element-plus'
|
||||
import { ElInput } from 'element-plus'
|
||||
import { useLockStore } from '@/store/modules/lock'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { useNow } from '@/hooks/web/useNow'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
import { Icon } from '@/components/Icon'
|
||||
import { useAuthStoreWithOut } from '@/store/modules/auth'
|
||||
import { useAuthStore } from '@/store/modules/auth'
|
||||
|
||||
const authStore = useAuthStoreWithOut()
|
||||
const authStore = useAuthStore()
|
||||
|
||||
const password = ref('')
|
||||
const loading = ref(false)
|
||||
@ -92,7 +92,7 @@ function handleShowForm(show = false) {
|
||||
{{ t('lock.message') }}
|
||||
</span>
|
||||
<div :class="`${prefixCls}-entry__footer enter-x`">
|
||||
<ElButton
|
||||
<BaseButton
|
||||
type="primary"
|
||||
size="small"
|
||||
class="mt-2 mr-2 enter-x"
|
||||
@ -101,8 +101,8 @@ function handleShowForm(show = false) {
|
||||
@click="handleShowForm(true)"
|
||||
>
|
||||
{{ t('common.back') }}
|
||||
</ElButton>
|
||||
<ElButton
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
type="primary"
|
||||
size="small"
|
||||
class="mt-2 mr-2 enter-x"
|
||||
@ -111,8 +111,8 @@ function handleShowForm(show = false) {
|
||||
@click="goLogin"
|
||||
>
|
||||
{{ t('lock.backToLogin') }}
|
||||
</ElButton>
|
||||
<ElButton
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
type="primary"
|
||||
class="mt-2"
|
||||
size="small"
|
||||
@ -121,7 +121,7 @@ function handleShowForm(show = false) {
|
||||
:disabled="loading"
|
||||
>
|
||||
{{ t('lock.entrySystem') }}
|
||||
</ElButton>
|
||||
</BaseButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -190,6 +190,7 @@ function handleShowForm(show = false) {
|
||||
font-size: 90px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: @screen-lg) {
|
||||
span:not(.meridiem) {
|
||||
font-size: 220px;
|
||||
@ -201,6 +202,7 @@ function handleShowForm(show = false) {
|
||||
font-size: 260px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: @screen-2xl) {
|
||||
span:not(.meridiem) {
|
||||
font-size: 320px;
|
||||
|
27
kinit-admin/src/components/VideoPlayer/index.ts
Normal file
27
kinit-admin/src/components/VideoPlayer/index.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { VNode, createVNode, render } from 'vue'
|
||||
import VideoPlayer from './src/VideoPlayer.vue'
|
||||
import { isClient } from '@/utils/is'
|
||||
import { VideoPlayerViewer } from '@/components/VideoPlayerViewer'
|
||||
import { toAnyString } from '@/utils'
|
||||
|
||||
export { VideoPlayer }
|
||||
|
||||
let instance: Nullable<VNode> = null
|
||||
|
||||
export function createVideoViewer(options: { url: string; poster?: string; show?: boolean }) {
|
||||
if (!isClient) return
|
||||
const { url, poster } = options
|
||||
|
||||
const propsData: Partial<{ url: string; poster?: string; show?: boolean; id?: string }> = {}
|
||||
const container = document.createElement('div')
|
||||
const id = toAnyString()
|
||||
container.id = id
|
||||
propsData.url = url
|
||||
propsData.poster = poster
|
||||
propsData.show = true
|
||||
propsData.id = id
|
||||
|
||||
document.body.appendChild(container)
|
||||
instance = createVNode(VideoPlayerViewer, propsData)
|
||||
render(instance, container)
|
||||
}
|
51
kinit-admin/src/components/VideoPlayer/src/VideoPlayer.vue
Normal file
51
kinit-admin/src/components/VideoPlayer/src/VideoPlayer.vue
Normal file
@ -0,0 +1,51 @@
|
||||
<script setup lang="ts">
|
||||
import Player from 'xgplayer'
|
||||
import { ref, unref, onMounted, watch, onBeforeUnmount, nextTick } from 'vue'
|
||||
import 'xgplayer/dist/index.min.css'
|
||||
const props = defineProps({
|
||||
url: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: true
|
||||
},
|
||||
poster: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
const playerRef = ref<Player>()
|
||||
const videoEl = ref<HTMLDivElement>()
|
||||
const intiPlayer = () => {
|
||||
if (!unref(videoEl)) return
|
||||
new Player({
|
||||
autoplay: false,
|
||||
...props,
|
||||
el: unref(videoEl)
|
||||
})
|
||||
}
|
||||
onMounted(() => {
|
||||
intiPlayer()
|
||||
})
|
||||
watch(
|
||||
() => props,
|
||||
async (newProps) => {
|
||||
await nextTick()
|
||||
if (newProps) {
|
||||
unref(playerRef)?.setConfig(newProps)
|
||||
}
|
||||
},
|
||||
{
|
||||
deep: true
|
||||
}
|
||||
)
|
||||
onBeforeUnmount(() => {
|
||||
unref(playerRef)?.destroy()
|
||||
})
|
||||
defineExpose({
|
||||
playerExpose: () => unref(playerRef)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="videoEl"></div>
|
||||
</template>
|
3
kinit-admin/src/components/VideoPlayerViewer/index.ts
Normal file
3
kinit-admin/src/components/VideoPlayerViewer/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import VideoPlayerViewer from './src/VideoPlayerViewer.vue'
|
||||
|
||||
export { VideoPlayerViewer }
|
@ -0,0 +1,46 @@
|
||||
<script setup lang="ts">
|
||||
import { VideoPlayer } from '@/components/VideoPlayer'
|
||||
import { ElOverlay } from 'element-plus'
|
||||
import { ref, nextTick } from 'vue'
|
||||
import { Icon } from '@/components/Icon'
|
||||
const props = defineProps({
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
url: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: true
|
||||
},
|
||||
poster: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
const visible = ref(props.show)
|
||||
const close = async () => {
|
||||
visible.value = false
|
||||
await nextTick()
|
||||
const wrap = document.getElementById(props.id)
|
||||
if (!wrap) return
|
||||
document.body.removeChild(wrap)
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<ElOverlay v-show="visible" @click="close">
|
||||
<div class="w-full h-full flex justify-center items-center relative" @click="close">
|
||||
<div
|
||||
class="w-44px h-44px color-[#fff] bg-[var(--el-text-color-regular)] rounded-full border-[#fff] flex justify-center items-center cursor-pointer absolute top-40px right-40px"
|
||||
@click="close"
|
||||
>
|
||||
<Icon icon="ep:close" :size="24" />
|
||||
</div>
|
||||
<VideoPlayer :url="url" :poster="poster" />
|
||||
</div>
|
||||
</ElOverlay>
|
||||
</template>
|
3
kinit-admin/src/components/Waterfall/index.ts
Normal file
3
kinit-admin/src/components/Waterfall/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import Waterfall from './src/Waterfall.vue'
|
||||
|
||||
export { Waterfall }
|
234
kinit-admin/src/components/Waterfall/src/Waterfall.vue
Normal file
234
kinit-admin/src/components/Waterfall/src/Waterfall.vue
Normal file
@ -0,0 +1,234 @@
|
||||
<script lang="ts" setup>
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
import { ref, nextTick, unref, onMounted, watch } from 'vue'
|
||||
import { useEventListener, useIntersectionObserver } from '@vueuse/core'
|
||||
import { debounce } from 'lodash-es'
|
||||
|
||||
const { getPrefixCls } = useDesign()
|
||||
|
||||
const prefixCls = getPrefixCls('waterfall')
|
||||
|
||||
const emit = defineEmits(['loadMore'])
|
||||
|
||||
const prop = defineProps({
|
||||
data: propTypes.arrayOf(propTypes.any),
|
||||
reset: propTypes.bool.def(true),
|
||||
width: propTypes.number.def(200),
|
||||
gap: propTypes.number.def(20),
|
||||
props: propTypes.objectOf(propTypes.string).def({
|
||||
src: 'src',
|
||||
height: 'height'
|
||||
}),
|
||||
cols: propTypes.number.def(undefined),
|
||||
loadingText: propTypes.string.def('加载中...'),
|
||||
loading: propTypes.bool.def(false),
|
||||
end: propTypes.bool.def(false),
|
||||
endText: propTypes.string.def('没有更多了'),
|
||||
autoCenter: propTypes.bool.def(true),
|
||||
layout: propTypes.oneOf(['javascript', 'flex']).def('flex')
|
||||
})
|
||||
|
||||
const wrapEl = ref<HTMLDivElement>()
|
||||
|
||||
const heights = ref<number[]>([])
|
||||
|
||||
const wrapHeight = ref(0)
|
||||
|
||||
const wrapWidth = ref(0)
|
||||
|
||||
const loadMore = ref<HTMLDivElement>()
|
||||
|
||||
// 首先确定列数 = 页面宽度 / 图片宽度
|
||||
const innerCols = ref(0)
|
||||
|
||||
const filterData = ref<any[]>([])
|
||||
|
||||
const filterWaterfall = async () => {
|
||||
filterData.value = []
|
||||
const { props, width, gap } = prop
|
||||
const data = prop.data as any[]
|
||||
await nextTick()
|
||||
|
||||
const container = unref(wrapEl) as HTMLElement
|
||||
if (!container) return
|
||||
innerCols.value = prop.cols ?? Math.floor(container.clientWidth / (width + gap))
|
||||
|
||||
const length = data.length
|
||||
for (let i = 0; i < length; i++) {
|
||||
if (i < unref(innerCols)) {
|
||||
heights.value[i] = data[i][props.height as string]
|
||||
filterData.value.push({
|
||||
...data[i],
|
||||
top: 0,
|
||||
left: i * (width + gap)
|
||||
})
|
||||
} else {
|
||||
// 其他行,先找出最矮的那一列 和 索引
|
||||
// 假设最小高度是第一个元素
|
||||
let minHeight = heights.value[0]
|
||||
let index = 0
|
||||
// 找出最小高度
|
||||
for (let j = 1; j < unref(innerCols); j++) {
|
||||
if (unref(heights)[j] < minHeight) {
|
||||
minHeight = unref(heights)[j]
|
||||
index = j
|
||||
}
|
||||
}
|
||||
|
||||
// 更新最矮高度
|
||||
heights.value[index] += data[i][props.height as string] + gap
|
||||
filterData.value.push({
|
||||
...data[i],
|
||||
top: minHeight + gap,
|
||||
left: index * (width + gap)
|
||||
})
|
||||
}
|
||||
}
|
||||
wrapHeight.value = Math.max(...unref(heights))
|
||||
wrapWidth.value = unref(innerCols) * (width + gap) - gap
|
||||
}
|
||||
|
||||
const flexWaterfall = async () => {
|
||||
const { width, gap } = prop
|
||||
const data = prop.data as any[]
|
||||
await nextTick()
|
||||
|
||||
const container = unref(wrapEl) as HTMLElement
|
||||
if (!container) return
|
||||
innerCols.value = prop.cols ?? Math.floor(container.clientWidth / (width + gap))
|
||||
|
||||
const length = data.length
|
||||
// 根据列数,创建数组
|
||||
const arr = new Array(unref(innerCols)).fill([])
|
||||
// 循环data,依次插入到arr中
|
||||
for (let i = 0; i < length; i++) {
|
||||
const index = i % unref(innerCols)
|
||||
arr[index] = [...arr[index], data[i]]
|
||||
}
|
||||
filterData.value = arr
|
||||
}
|
||||
|
||||
const initLayout = () => {
|
||||
const { layout } = prop
|
||||
if (layout === 'javascript') {
|
||||
filterWaterfall()
|
||||
} else if (layout === 'flex') {
|
||||
flexWaterfall()
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => [prop.data, prop.cols],
|
||||
() => {
|
||||
initLayout()
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
if (unref(prop.reset)) {
|
||||
useEventListener(window, 'resize', debounce(initLayout, 300))
|
||||
}
|
||||
useIntersectionObserver(
|
||||
unref(loadMore),
|
||||
([{ isIntersecting }]) => {
|
||||
if (isIntersecting && !prop.loading && !prop.end) {
|
||||
emit('loadMore')
|
||||
}
|
||||
},
|
||||
{
|
||||
threshold: 0.1
|
||||
}
|
||||
)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:class="[
|
||||
prefixCls,
|
||||
'flex',
|
||||
'items-center',
|
||||
{
|
||||
'justify-center': autoCenter
|
||||
}
|
||||
]"
|
||||
ref="wrapEl"
|
||||
:style="{
|
||||
height: `${layout === 'javascript' ? wrapHeight + 40 : 'auto'}px`
|
||||
}"
|
||||
>
|
||||
<template v-if="layout === 'javascript'">
|
||||
<div class="relative" :style="{ width: `${wrapWidth}px`, height: `${wrapHeight + 40}px` }">
|
||||
<div
|
||||
v-for="(item, $index) in filterData"
|
||||
:class="[
|
||||
`${prefixCls}-item__${$index}`,
|
||||
{
|
||||
absolute: layout === 'javascript'
|
||||
}
|
||||
]"
|
||||
:key="`water-${$index}`"
|
||||
:style="{
|
||||
width: `${width}px`,
|
||||
height: `${item[props.height as string]}px`,
|
||||
top: `${item.top}px`,
|
||||
left: `${item.left}px`
|
||||
}"
|
||||
>
|
||||
<img :src="item[props.src as string]" class="w-full h-full block" alt="" srcset="" />
|
||||
</div>
|
||||
<div
|
||||
ref="loadMore"
|
||||
class="h-40px flex justify-center absolute w-full"
|
||||
:style="{
|
||||
top: `${wrapHeight + gap}px`
|
||||
}"
|
||||
>
|
||||
{{ end ? endText : loadingText }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="layout === 'flex'">
|
||||
<div
|
||||
class="relative flex pb-40px"
|
||||
:style="{
|
||||
width: cols ? '100%' : 'auto'
|
||||
}"
|
||||
>
|
||||
<div
|
||||
v-for="(item, $index) in filterData"
|
||||
:key="`waterWrap-${$index}`"
|
||||
class="flex-1"
|
||||
:style="{
|
||||
marginRight: $index === filterData.length - 1 ? '0' : `${gap}px`
|
||||
}"
|
||||
>
|
||||
<div
|
||||
v-for="(child, i) in item"
|
||||
:key="`waterWrap-${$index}-${i}`"
|
||||
:style="{
|
||||
marginBottom: `${gap}px`,
|
||||
width: cols ? '100%' : `${width}px`,
|
||||
height: cols ? 'auto' : `${child[props.height as string]}px`
|
||||
}"
|
||||
>
|
||||
<img :src="child[props.src as string]" class="w-full h-full block" alt="" srcset="" />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
ref="loadMore"
|
||||
class="h-40px flex justify-center absolute w-full items-center"
|
||||
:style="{
|
||||
bottom: 0
|
||||
}"
|
||||
>
|
||||
{{ end ? endText : loadingText }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
@ -1,8 +1,10 @@
|
||||
import type { App } from 'vue'
|
||||
import { Icon } from './Icon'
|
||||
import { Permission } from './Permission'
|
||||
import { BaseButton } from './Button'
|
||||
|
||||
export const setupGlobCom = (app: App<Element>): void => {
|
||||
app.component('Icon', Icon)
|
||||
app.component('Permission', Permission)
|
||||
app.component('BaseButton', BaseButton)
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
import axios, { AxiosInstance, InternalAxiosRequestConfig, AxiosResponse, AxiosError } from 'axios'
|
||||
import { useStorage } from '@/hooks/web/useStorage'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
import { useAuthStore } from '@/store/modules/auth'
|
||||
import { useAuthStoreWithOut } from '@/store/modules/auth'
|
||||
import qs from 'qs'
|
||||
import { config } from './config'
|
||||
import { ElMessage } from 'element-plus'
|
||||
@ -9,8 +7,6 @@ import request from '@/config/axios'
|
||||
|
||||
const { result_code, unauthorized_code, request_timeout } = config
|
||||
|
||||
const { getStorage, setStorage } = useStorage()
|
||||
|
||||
// 创建axios实例
|
||||
const service: AxiosInstance = axios.create({
|
||||
baseURL: '/api', // api 的 base_url
|
||||
@ -21,10 +17,10 @@ const service: AxiosInstance = axios.create({
|
||||
// request拦截器
|
||||
service.interceptors.request.use(
|
||||
(config: InternalAxiosRequestConfig) => {
|
||||
const appStore = useAppStore()
|
||||
const token = getStorage(appStore.getToken)
|
||||
const authStore = useAuthStoreWithOut()
|
||||
const token = authStore.getToken
|
||||
if (token !== '') {
|
||||
;(config.headers as any)['Authorization'] = token // 让每个请求携带自定义token 请根据实际情况自行修改
|
||||
;(config.headers as any)[authStore.getTokenKey ?? 'Authorization'] = token // 让每个请求携带自定义token 请根据实际情况自行修改
|
||||
}
|
||||
if (
|
||||
config.method === 'post' &&
|
||||
@ -87,18 +83,18 @@ service.interceptors.response.use(
|
||||
if (refresh === '1') {
|
||||
// 因token快过期,刷新token
|
||||
refreshToken().then((res) => {
|
||||
const appStore = useAppStore()
|
||||
setStorage(appStore.getToken, `${res.data.token_type} ${res.data.access_token}`)
|
||||
setStorage(appStore.getRefreshToken, res.data.refresh_token)
|
||||
const authStore = useAuthStoreWithOut()
|
||||
authStore.setToken(`${res.data.token_type} ${res.data.access_token}`)
|
||||
authStore.setRefreshToken(res.data.refresh_token)
|
||||
})
|
||||
}
|
||||
return response.data
|
||||
} else if (code === unauthorized_code) {
|
||||
// 因token无效,token过期导致
|
||||
refreshToken().then((res) => {
|
||||
const appStore = useAppStore()
|
||||
setStorage(appStore.getToken, `${res.data.token_type} ${res.data.access_token}`)
|
||||
setStorage(appStore.getRefreshToken, res.data.refresh_token)
|
||||
const authStore = useAuthStoreWithOut()
|
||||
authStore.setToken(`${res.data.token_type} ${res.data.access_token}`)
|
||||
authStore.setRefreshToken(res.data.refresh_token)
|
||||
ElMessage.error('操作失败,请重试')
|
||||
})
|
||||
} else {
|
||||
@ -108,7 +104,7 @@ service.interceptors.response.use(
|
||||
(error: AxiosError) => {
|
||||
console.log('err', error)
|
||||
let { message } = error
|
||||
const authStore = useAuthStore()
|
||||
const authStore = useAuthStoreWithOut()
|
||||
const status = error.response?.status
|
||||
switch (status) {
|
||||
case 400:
|
||||
@ -158,8 +154,8 @@ service.interceptors.response.use(
|
||||
|
||||
// 刷新Token
|
||||
const refreshToken = (): Promise<IResponse> => {
|
||||
const appStore = useAppStore()
|
||||
const data = getStorage(appStore.getRefreshToken)
|
||||
const authStore = useAuthStoreWithOut()
|
||||
const data = authStore.getRefreshToken
|
||||
return request.post({ url: '/auth/token/refresh', data })
|
||||
}
|
||||
|
||||
|
@ -5,11 +5,11 @@ import { isArray } from '@/utils/is'
|
||||
import { useAuthStoreWithOut } from '@/store/modules/auth'
|
||||
|
||||
const { t } = useI18n()
|
||||
const authStore = useAuthStoreWithOut()
|
||||
|
||||
// 全部权限
|
||||
const all_permission = ['*.*.*']
|
||||
const hasPermission = (value: string | string[]): boolean => {
|
||||
const authStore = useAuthStoreWithOut()
|
||||
const permissions = authStore.getPermissions
|
||||
if (!value) {
|
||||
throw new Error(t('permission.hasPermission'))
|
||||
|
47
kinit-admin/src/hooks/web/useClipboard.ts
Normal file
47
kinit-admin/src/hooks/web/useClipboard.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import { ref } from 'vue'
|
||||
|
||||
const useClipboard = () => {
|
||||
const copied = ref(false)
|
||||
const text = ref('')
|
||||
const isSupported = ref(false)
|
||||
|
||||
if (!navigator.clipboard && !document.execCommand) {
|
||||
isSupported.value = false
|
||||
} else {
|
||||
isSupported.value = true
|
||||
}
|
||||
|
||||
const copy = (str: string) => {
|
||||
if (navigator.clipboard) {
|
||||
navigator.clipboard.writeText(str).then(() => {
|
||||
text.value = str
|
||||
copied.value = true
|
||||
resetCopied()
|
||||
})
|
||||
return
|
||||
}
|
||||
const input = document.createElement('input')
|
||||
input.setAttribute('readonly', 'readonly')
|
||||
input.setAttribute('value', str)
|
||||
document.body.appendChild(input)
|
||||
input.select()
|
||||
input.setSelectionRange(0, 9999)
|
||||
if (document.execCommand('copy')) {
|
||||
text.value = str
|
||||
document.execCommand('copy')
|
||||
copied.value = true
|
||||
resetCopied()
|
||||
}
|
||||
document.body.removeChild(input)
|
||||
}
|
||||
|
||||
const resetCopied = () => {
|
||||
setTimeout(() => {
|
||||
copied.value = false
|
||||
}, 1500)
|
||||
}
|
||||
|
||||
return { copy, text, copied, isSupported }
|
||||
}
|
||||
|
||||
export { useClipboard }
|
@ -79,19 +79,14 @@ const filterSearchSchema = (crudSchema: CrudSchema[]): FormSchema[] => {
|
||||
for (let i = 0; i < length; i++) {
|
||||
const schemaItem = crudSchema[i]
|
||||
// 判断是否隐藏
|
||||
if (!schemaItem?.search?.hidden) {
|
||||
const searchSchemaItem = {
|
||||
component: schemaItem?.search?.component || 'Input',
|
||||
...schemaItem.search,
|
||||
field: schemaItem.field,
|
||||
label: schemaItem.search?.label || schemaItem.label
|
||||
}
|
||||
|
||||
// 删除不必要的字段
|
||||
delete searchSchemaItem.hidden
|
||||
|
||||
searchSchema.push(searchSchemaItem)
|
||||
const searchSchemaItem = {
|
||||
component: schemaItem?.search?.component || 'Input',
|
||||
...schemaItem.search,
|
||||
field: schemaItem.field,
|
||||
label: schemaItem.search?.label || schemaItem.label
|
||||
}
|
||||
|
||||
searchSchema.push(searchSchemaItem)
|
||||
}
|
||||
|
||||
return searchSchema
|
||||
@ -127,19 +122,14 @@ const filterFormSchema = (crudSchema: CrudSchema[]): FormSchema[] => {
|
||||
for (let i = 0; i < length; i++) {
|
||||
const formItem = crudSchema[i]
|
||||
// 判断是否隐藏
|
||||
if (!formItem?.form?.hidden) {
|
||||
const formSchemaItem = {
|
||||
component: formItem?.form?.component || 'Input',
|
||||
...formItem.form,
|
||||
field: formItem.field,
|
||||
label: formItem.form?.label || formItem.label
|
||||
}
|
||||
|
||||
// 删除不必要的字段
|
||||
delete formSchemaItem.hidden
|
||||
|
||||
formSchema.push(formSchemaItem)
|
||||
const formSchemaItem = {
|
||||
component: formItem?.form?.component || 'Input',
|
||||
...formItem.form,
|
||||
field: formItem.field,
|
||||
label: formItem.form?.label || formItem.label
|
||||
}
|
||||
|
||||
formSchema.push(formSchemaItem)
|
||||
}
|
||||
|
||||
return formSchema
|
||||
|
@ -2,6 +2,7 @@ import type { Form, FormExpose } from '@/components/Form'
|
||||
import type { ElForm, ElFormItem } from 'element-plus'
|
||||
import { ref, unref, nextTick } from 'vue'
|
||||
import { FormSchema, FormSetProps, FormProps } from '@/components/Form'
|
||||
import { isEmptyVal, isObject } from '@/utils/is'
|
||||
|
||||
export const useForm = () => {
|
||||
// From实例
|
||||
@ -93,9 +94,27 @@ export const useForm = () => {
|
||||
* @description 获取表单数据
|
||||
* @returns form data
|
||||
*/
|
||||
getFormData: async <T = Recordable>(): Promise<T> => {
|
||||
getFormData: async <T = Recordable>(filterEmptyVal = true): Promise<T> => {
|
||||
const form = await getForm()
|
||||
return form?.formModel as T
|
||||
const model = form?.formModel as any
|
||||
if (filterEmptyVal) {
|
||||
// 使用reduce过滤空值,并返回一个新对象
|
||||
return Object.keys(model).reduce((prev, next) => {
|
||||
const value = model[next]
|
||||
if (!isEmptyVal(value)) {
|
||||
if (isObject(value)) {
|
||||
if (Object.keys(value).length > 0) {
|
||||
prev[next] = value
|
||||
}
|
||||
} else {
|
||||
prev[next] = value
|
||||
}
|
||||
}
|
||||
return prev
|
||||
}, {}) as T
|
||||
} else {
|
||||
return model as T
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
21
kinit-admin/src/hooks/web/useNetwork.ts
Normal file
21
kinit-admin/src/hooks/web/useNetwork.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { ref, onBeforeUnmount } from 'vue'
|
||||
|
||||
const useNetwork = () => {
|
||||
const online = ref(true)
|
||||
|
||||
const updateNetwork = () => {
|
||||
online.value = navigator.onLine
|
||||
}
|
||||
|
||||
window.addEventListener('online', updateNetwork)
|
||||
window.addEventListener('offline', updateNetwork)
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('online', updateNetwork)
|
||||
window.removeEventListener('offline', updateNetwork)
|
||||
})
|
||||
|
||||
return { online }
|
||||
}
|
||||
|
||||
export { useNetwork }
|
@ -1,13 +1,15 @@
|
||||
import { useAppStoreWithOut } from '@/store/modules/app'
|
||||
|
||||
const appStore = useAppStoreWithOut()
|
||||
|
||||
export const usePageLoading = () => {
|
||||
const loadStart = () => {
|
||||
const appStore = useAppStoreWithOut()
|
||||
|
||||
appStore.setPageLoading(true)
|
||||
}
|
||||
|
||||
const loadDone = () => {
|
||||
const appStore = useAppStoreWithOut()
|
||||
|
||||
appStore.setPageLoading(false)
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@ const getValueType = (value: any) => {
|
||||
return type.slice(8, -1)
|
||||
}
|
||||
|
||||
export const useStorage = (type: 'sessionStorage' | 'localStorage' = 'sessionStorage') => {
|
||||
export const useStorage = (type: 'sessionStorage' | 'localStorage' = 'localStorage') => {
|
||||
const setStorage = (key: string, value: any) => {
|
||||
const valueType = getValueType(value)
|
||||
window[type].setItem(key, JSON.stringify({ type: valueType, value }))
|
||||
|
@ -3,10 +3,10 @@ import { isString } from '@/utils/is'
|
||||
import { useAppStoreWithOut } from '@/store/modules/app'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
|
||||
const appStore = useAppStoreWithOut()
|
||||
|
||||
export const useTitle = (newTitle?: string) => {
|
||||
const { t } = useI18n()
|
||||
const appStore = useAppStoreWithOut()
|
||||
|
||||
const title = ref(
|
||||
newTitle ? `${appStore.getTitle} - ${t(newTitle as string)}` : appStore.getTitle
|
||||
)
|
||||
|
@ -71,8 +71,14 @@ export default defineComponent({
|
||||
|
||||
.@{prefix-cls} {
|
||||
background-color: var(--app-content-bg-color);
|
||||
:deep(.@{elNamespace}-scrollbar__view) {
|
||||
height: 100% !important;
|
||||
.@{prefix-cls}-content-scrollbar {
|
||||
& > :deep(.el-scrollbar__wrap) {
|
||||
& > .@{elNamespace}-scrollbar__view {
|
||||
display: flex;
|
||||
height: 100% !important;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -6,10 +6,6 @@ import { computed } from 'vue'
|
||||
|
||||
const appStore = useAppStore()
|
||||
|
||||
const layout = computed(() => appStore.getLayout)
|
||||
|
||||
const fixedHeader = computed(() => appStore.getFixedHeader)
|
||||
|
||||
const footer = computed(() => appStore.getFooter)
|
||||
|
||||
const tagsViewStore = useTagsViewStore()
|
||||
@ -17,39 +13,12 @@ const tagsViewStore = useTagsViewStore()
|
||||
const getCaches = computed((): string[] => {
|
||||
return tagsViewStore.getCachedViews
|
||||
})
|
||||
|
||||
const tagsView = computed(() => appStore.getTagsView)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section
|
||||
:class="[
|
||||
'p-[var(--app-content-padding)] w-[calc(100%-var(--app-content-padding)-var(--app-content-padding))] bg-[var(--app-content-bg-color-new)] dark:bg-[var(--el-bg-color)]',
|
||||
{
|
||||
'!min-h-[calc(100%-var(--app-footer-height))]':
|
||||
(fixedHeader &&
|
||||
(layout === 'classic' || layout === 'topLeft' || layout === 'top') &&
|
||||
footer) ||
|
||||
(!tagsView && layout === 'top' && footer),
|
||||
|
||||
'!min-h-[calc(100%-var(--app-content-padding)-var(--app-content-padding)-var(--app-footer-height)-var(--tags-view-height))]':
|
||||
tagsView && layout === 'top' && footer,
|
||||
|
||||
'!min-h-[calc(100%-var(--tags-view-height)-var(--app-content-padding)-var(--app-content-padding)-var(--top-tool-height)-var(--app-footer-height))]':
|
||||
!fixedHeader && layout === 'classic' && footer,
|
||||
|
||||
'!min-h-[calc(100%-var(--tags-view-height)-var(--app-content-padding)-var(--app-content-padding)-var(--app-footer-height))]':
|
||||
!fixedHeader && layout === 'topLeft' && footer,
|
||||
|
||||
// '!min-h-[calc(100%-var(--app-content-padding)-var(--app-content-padding)-var(--app-footer-height)-var(--tags-view-height)-var(--top-tool-height))]':
|
||||
// !fixedHeader && layout === 'top' && footer,
|
||||
|
||||
'!min-h-[calc(100%-var(--top-tool-height)-var(--app-content-padding)-var(--app-content-padding))]':
|
||||
fixedHeader && layout === 'cutMenu' && footer,
|
||||
|
||||
'!min-h-[calc(100%-var(--top-tool-height)-var(--app-content-padding)-var(--app-content-padding)-var(--tags-view-height))]':
|
||||
!fixedHeader && layout === 'cutMenu' && footer
|
||||
}
|
||||
'flex-1 p-[var(--app-content-padding)] w-[calc(100%-var(--app-content-padding)-var(--app-content-padding))] bg-[var(--app-content-bg-color)] dark:bg-[var(--el-bg-color)]'
|
||||
]"
|
||||
>
|
||||
<router-view>
|
||||
|
@ -42,8 +42,7 @@ export default defineComponent({
|
||||
id={`${variables.namespace}-tool-header`}
|
||||
class={[
|
||||
prefixCls,
|
||||
'h-[var(--top-tool-height)] relative px-[var(--top-tool-p-x)] flex items-center justify-between',
|
||||
'dark:bg-[var(--el-bg-color)]'
|
||||
'h-[var(--top-tool-height)] relative px-[var(--top-tool-p-x)] flex items-center justify-between'
|
||||
]}
|
||||
>
|
||||
{layout.value !== 'top' ? (
|
||||
|
@ -195,8 +195,8 @@ export const useRenderLayout = () => {
|
||||
`${prefixCls}-content`,
|
||||
'w-full',
|
||||
{
|
||||
'h-[calc(100%-var(--app-footer-height))]': !fixedHeader.value,
|
||||
'h-[calc(100%-var(--tags-view-height)-var(--app-footer-height))]': fixedHeader.value
|
||||
'h-[calc(100%-var(--top-tool-height))]': !fixedHeader.value,
|
||||
'h-[calc(100%-var(--tags-view-height)-var(--top-tool-height))]': fixedHeader.value
|
||||
}
|
||||
]}
|
||||
>
|
||||
|
@ -50,6 +50,8 @@ export default {
|
||||
notSpace: 'Spaces are not allowed',
|
||||
notSpecialCharacters: 'Special characters are not allowed',
|
||||
isEqual: 'The two are not equal',
|
||||
// 列设置
|
||||
setting: 'Setting',
|
||||
selectAll: 'Select all',
|
||||
SerialNumberColumn: 'Index column'
|
||||
},
|
||||
@ -187,7 +189,14 @@ export default {
|
||||
function: 'Function',
|
||||
multipleTabs: 'Multiple tabs',
|
||||
details: 'Details',
|
||||
iconPicker: 'Icon picker'
|
||||
iconPicker: 'Icon picker',
|
||||
request: 'Request',
|
||||
waterfall: 'Waterfall',
|
||||
imageCropping: 'Image cropping',
|
||||
videoPlayer: 'Video player',
|
||||
// 表格视频预览
|
||||
tableVideoPreview: 'Table video preview',
|
||||
cardTable: 'Card table'
|
||||
},
|
||||
permission: {
|
||||
hasPermission: 'Please set the operation permission value'
|
||||
@ -333,7 +342,8 @@ export default {
|
||||
lazyLoad: 'Lazy load',
|
||||
upload: 'Upload',
|
||||
// 用户头像
|
||||
userAvatar: 'User avatar'
|
||||
userAvatar: 'User avatar',
|
||||
iconPicker: 'Icon picker'
|
||||
},
|
||||
guideDemo: {
|
||||
guide: 'Guide',
|
||||
@ -462,7 +472,9 @@ export default {
|
||||
fixedHeaderOrAuto: 'Fixed header or auto',
|
||||
getSelections: 'Get selections',
|
||||
preview: 'Preview',
|
||||
showOrHiddenSortable: 'Show or hidden sortable'
|
||||
showOrHiddenSortable: 'Show or hidden sortable',
|
||||
videoPreview: 'Video preview',
|
||||
cardTable: 'Card table'
|
||||
},
|
||||
richText: {
|
||||
richText: 'Rich text',
|
||||
|
@ -50,6 +50,7 @@ export default {
|
||||
notSpace: '不能包含空格',
|
||||
notSpecialCharacters: '不能包含特殊字符',
|
||||
isEqual: '两次输入不一致',
|
||||
setting: '设置',
|
||||
selectAll: '全选',
|
||||
SerialNumberColumn: '序号列'
|
||||
},
|
||||
@ -84,7 +85,7 @@ export default {
|
||||
sizeIcon: '尺寸图标',
|
||||
localeIcon: '多语言图标',
|
||||
tagsView: '标签页',
|
||||
logo: '标志',
|
||||
logo: 'Logo',
|
||||
greyMode: '灰色模式',
|
||||
fixedHeader: '固定头部',
|
||||
headerTheme: '头部主题',
|
||||
@ -185,7 +186,13 @@ export default {
|
||||
function: '功能',
|
||||
multipleTabs: '多开标签页',
|
||||
details: '详情页',
|
||||
iconPicker: '图标选择器'
|
||||
iconPicker: '图标选择器',
|
||||
request: '请求',
|
||||
waterfall: '瀑布流',
|
||||
imageCropping: '图片裁剪',
|
||||
videoPlayer: '视频播放器',
|
||||
tableVideoPreview: '表格视频预览',
|
||||
cardTable: '卡片表格'
|
||||
},
|
||||
permission: {
|
||||
hasPermission: '请设置操作权限值'
|
||||
@ -328,7 +335,8 @@ export default {
|
||||
customContent: '自定义内容',
|
||||
lazyLoad: '懒加载',
|
||||
upload: '上传',
|
||||
userAvatar: '用户头像'
|
||||
userAvatar: '用户头像',
|
||||
iconPicker: '图标选择器'
|
||||
},
|
||||
guideDemo: {
|
||||
guide: '引导页',
|
||||
@ -455,7 +463,9 @@ export default {
|
||||
fixedHeaderOrAuto: '固定头部/自动',
|
||||
getSelections: '获取多选数据',
|
||||
preview: '封面',
|
||||
showOrHiddenSortable: '显示/隐藏排序'
|
||||
showOrHiddenSortable: '显示/隐藏排序',
|
||||
videoPreview: '视频预览',
|
||||
cardTable: '卡片表格'
|
||||
},
|
||||
richText: {
|
||||
richText: '富文本',
|
||||
@ -531,7 +541,7 @@ export default {
|
||||
menu: {
|
||||
menuName: '菜单名称',
|
||||
icon: '图标',
|
||||
permission: '权限标识',
|
||||
permission: '按钮权限',
|
||||
component: '组件',
|
||||
path: '路径',
|
||||
status: '状态',
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'vue/jsx'
|
||||
|
||||
// 引入windi css
|
||||
import '@/plugins/unocss'
|
||||
|
||||
|
@ -1,6 +1,4 @@
|
||||
import router from './router'
|
||||
import { useAppStoreWithOut } from '@/store/modules/app'
|
||||
import { useStorage } from '@/hooks/web/useStorage'
|
||||
import type { RouteRecordRaw } from 'vue-router'
|
||||
import { useTitle } from '@/hooks/web/useTitle'
|
||||
import { useNProgress } from '@/hooks/web/useNProgress'
|
||||
@ -9,13 +7,6 @@ import { usePageLoading } from '@/hooks/web/usePageLoading'
|
||||
import { useAuthStoreWithOut } from '@/store/modules/auth'
|
||||
import { getRoleMenusApi } from '@/api/login'
|
||||
|
||||
const permissionStore = usePermissionStoreWithOut()
|
||||
|
||||
const appStore = useAppStoreWithOut()
|
||||
const authStore = useAuthStoreWithOut()
|
||||
|
||||
const { getStorage, setStorage } = useStorage()
|
||||
|
||||
const { start, done } = useNProgress()
|
||||
|
||||
const { loadStart, loadDone } = usePageLoading()
|
||||
@ -25,7 +16,9 @@ const whiteList = ['/login'] // 不重定向白名单
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
start()
|
||||
loadStart()
|
||||
if (getStorage(appStore.getToken)) {
|
||||
const permissionStore = usePermissionStoreWithOut()
|
||||
const authStore = useAuthStoreWithOut()
|
||||
if (authStore.getToken) {
|
||||
if (to.path === '/login') {
|
||||
next({ path: '/' })
|
||||
} else if (to.path === '/reset/password') {
|
||||
@ -42,7 +35,6 @@ router.beforeEach(async (to, from, next) => {
|
||||
// 开发者可根据实际情况进行修改
|
||||
const res = await getRoleMenusApi()
|
||||
const routers = res.data || []
|
||||
setStorage('roleRouters', routers)
|
||||
await permissionStore.generateRoutes(routers).catch(() => {})
|
||||
permissionStore.getAddRouters.forEach((route) => {
|
||||
router.addRoute(route as RouteRecordRaw) // 动态添加可访问路由表
|
||||
|
@ -12,7 +12,13 @@ export const setupElementPlus = (app: App<Element>) => {
|
||||
app.use(plugin)
|
||||
})
|
||||
|
||||
// 为了开发环境启动更快,一次性引入所有样式
|
||||
if (import.meta.env.VITE_USE_ALL_ELEMENT_PLUS_STYLE === 'true') {
|
||||
import('element-plus/dist/index.css')
|
||||
return
|
||||
}
|
||||
|
||||
components.forEach((component) => {
|
||||
app.component(component.name, component)
|
||||
app.component(component.name!, component)
|
||||
})
|
||||
}
|
||||
|
@ -1,3 +1 @@
|
||||
import 'virtual:svg-icons-register'
|
||||
|
||||
import '@purge-icons/generated'
|
||||
|
@ -115,7 +115,7 @@ const router = createRouter({
|
||||
})
|
||||
|
||||
export const resetRouter = (): void => {
|
||||
const resetWhiteNameList = ['Login', 'NoFind', 'Root']
|
||||
const resetWhiteNameList = ['Login', 'NoFind', 'Root', 'ResetPassword', 'Redirect']
|
||||
router.getRoutes().forEach((route) => {
|
||||
const { name } = route
|
||||
if (name && !resetWhiteNameList.includes(name as string)) {
|
||||
|
@ -1,10 +1,12 @@
|
||||
import type { App } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
import piniaPersist from 'pinia-plugin-persist'
|
||||
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
|
||||
|
||||
// pinia-plugin-persistedstate 持久化存储官方文档:https://prazdevs.github.io/pinia-plugin-persistedstate/zh/guide/
|
||||
|
||||
const store = createPinia()
|
||||
|
||||
store.use(piniaPersist)
|
||||
store.use(piniaPluginPersistedstate)
|
||||
|
||||
export const setupStore = (app: App<Element>) => {
|
||||
app.use(store)
|
||||
|
@ -2,9 +2,10 @@ import { defineStore } from 'pinia'
|
||||
import { store } from '../index'
|
||||
import { setCssVar, humpToUnderline } from '@/utils'
|
||||
import { ElMessage, ComponentSize } from 'element-plus'
|
||||
import { useStorage } from '@/hooks/web/useStorage'
|
||||
|
||||
const { getStorage, setStorage } = useStorage()
|
||||
import { colorIsDark, hexToRGB, lighten, mix } from '@/utils/color'
|
||||
import { useCssVar } from '@vueuse/core'
|
||||
import { unref } from 'vue'
|
||||
import { useDark } from '@vueuse/core'
|
||||
|
||||
interface AppState {
|
||||
breadcrumb: boolean
|
||||
@ -25,7 +26,6 @@ interface AppState {
|
||||
pageLoading: boolean
|
||||
layout: LayoutType
|
||||
title: string
|
||||
userInfo: string
|
||||
isDark: boolean
|
||||
currentSize: ComponentSize
|
||||
sizeMap: ComponentSize[]
|
||||
@ -34,8 +34,6 @@ interface AppState {
|
||||
theme: ThemeTypes
|
||||
fixedMenu: boolean
|
||||
|
||||
token: string
|
||||
refreshToken: string
|
||||
logoImage: string
|
||||
footerContent: string
|
||||
icpNumber: string
|
||||
@ -44,7 +42,6 @@ interface AppState {
|
||||
export const useAppStore = defineStore('app', {
|
||||
state: (): AppState => {
|
||||
return {
|
||||
userInfo: 'userInfo', // 登录信息存储字段-建议每个项目换一个字段,避免与其它项目冲突
|
||||
sizeMap: ['default', 'large', 'small'],
|
||||
mobile: false, // 是否是移动端
|
||||
title: import.meta.env.VITE_APP_TITLE, // 标题
|
||||
@ -63,14 +60,14 @@ export const useAppStore = defineStore('app', {
|
||||
fixedHeader: true, // 固定toolheader
|
||||
footer: true, // 显示页脚
|
||||
greyMode: false, // 是否开始灰色模式,用于特殊悼念日
|
||||
dynamicRouter: getStorage('dynamicRouter'), // 是否动态路由
|
||||
serverDynamicRouter: getStorage('serverDynamicRouter'), // 是否服务端渲染动态路由
|
||||
fixedMenu: getStorage('fixedMenu'), // 是否固定菜单
|
||||
dynamicRouter: true, // 是否动态路由
|
||||
serverDynamicRouter: true, // 是否服务端渲染动态路由
|
||||
fixedMenu: false, // 是否固定菜单
|
||||
|
||||
layout: getStorage('layout') || 'classic', // layout布局
|
||||
isDark: getStorage('isDark'), // 是否是暗黑模式
|
||||
currentSize: getStorage('default') || 'default', // 组件尺寸
|
||||
theme: getStorage('theme') || {
|
||||
layout: 'classic', // layout布局
|
||||
isDark: false, // 是否是暗黑模式
|
||||
currentSize: 'default', // 组件尺寸
|
||||
theme: {
|
||||
// 主题色
|
||||
elColorPrimary: '#409eff',
|
||||
// 左侧菜单边框颜色
|
||||
@ -101,8 +98,6 @@ export const useAppStore = defineStore('app', {
|
||||
topToolBorderColor: '#eee'
|
||||
},
|
||||
|
||||
token: 'Token', // 存储Token字段
|
||||
refreshToken: 'RefreshToken', // 存储刷新Token字段
|
||||
logoImage: '', // logo图片
|
||||
footerContent: '', // 页脚内容
|
||||
icpNumber: '' // 备案号
|
||||
@ -166,9 +161,6 @@ export const useAppStore = defineStore('app', {
|
||||
getTitle(): string {
|
||||
return this.title
|
||||
},
|
||||
getUserInfo(): string {
|
||||
return this.userInfo
|
||||
},
|
||||
getIsDark(): boolean {
|
||||
return this.isDark
|
||||
},
|
||||
@ -191,12 +183,6 @@ export const useAppStore = defineStore('app', {
|
||||
getLogoImage(): string {
|
||||
return this.logoImage
|
||||
},
|
||||
getToken(): string {
|
||||
return this.token
|
||||
},
|
||||
getRefreshToken(): string {
|
||||
return this.refreshToken
|
||||
},
|
||||
getFooterContent(): string {
|
||||
return this.footerContent
|
||||
},
|
||||
@ -245,15 +231,12 @@ export const useAppStore = defineStore('app', {
|
||||
this.greyMode = greyMode
|
||||
},
|
||||
setDynamicRouter(dynamicRouter: boolean) {
|
||||
setStorage('dynamicRouter', dynamicRouter)
|
||||
this.dynamicRouter = dynamicRouter
|
||||
},
|
||||
setServerDynamicRouter(serverDynamicRouter: boolean) {
|
||||
setStorage('serverDynamicRouter', serverDynamicRouter)
|
||||
this.serverDynamicRouter = serverDynamicRouter
|
||||
},
|
||||
setFixedMenu(fixedMenu: boolean) {
|
||||
setStorage('fixedMenu', fixedMenu)
|
||||
this.fixedMenu = fixedMenu
|
||||
},
|
||||
setPageLoading(pageLoading: boolean) {
|
||||
@ -265,7 +248,6 @@ export const useAppStore = defineStore('app', {
|
||||
return
|
||||
}
|
||||
this.layout = layout
|
||||
setStorage('layout', this.layout)
|
||||
},
|
||||
setTitle(title: string) {
|
||||
this.title = title
|
||||
@ -279,23 +261,22 @@ export const useAppStore = defineStore('app', {
|
||||
document.documentElement.classList.add('light')
|
||||
document.documentElement.classList.remove('dark')
|
||||
}
|
||||
setStorage('isDark', this.isDark)
|
||||
this.setPrimaryLight()
|
||||
},
|
||||
setCurrentSize(currentSize: ComponentSize) {
|
||||
this.currentSize = currentSize
|
||||
setStorage('currentSize', this.currentSize)
|
||||
},
|
||||
setMobile(mobile: boolean) {
|
||||
this.mobile = mobile
|
||||
},
|
||||
setTheme(theme: ThemeTypes) {
|
||||
this.theme = Object.assign(this.theme, theme)
|
||||
setStorage('theme', this.theme)
|
||||
},
|
||||
setCssVarTheme() {
|
||||
for (const key in this.theme) {
|
||||
setCssVar(`--${humpToUnderline(key)}`, this.theme[key])
|
||||
}
|
||||
this.setPrimaryLight()
|
||||
},
|
||||
setFooter(footer: boolean) {
|
||||
this.footer = footer
|
||||
@ -309,8 +290,75 @@ export const useAppStore = defineStore('app', {
|
||||
},
|
||||
setIcpNumber(icpNumber: string) {
|
||||
this.icpNumber = icpNumber
|
||||
},
|
||||
setPrimaryLight() {
|
||||
if (this.theme.elColorPrimary) {
|
||||
const elColorPrimary = this.theme.elColorPrimary
|
||||
const color = this.isDark ? '#000000' : '#ffffff'
|
||||
const lightList = [3, 5, 7, 8, 9]
|
||||
lightList.forEach((v) => {
|
||||
setCssVar(`--el-color-primary-light-${v}`, mix(color, elColorPrimary, v / 10))
|
||||
})
|
||||
setCssVar(`--el-color-primary-dark-2`, mix(color, elColorPrimary, 0.2))
|
||||
}
|
||||
},
|
||||
setMenuTheme(color: string) {
|
||||
const primaryColor = useCssVar('--el-color-primary', document.documentElement)
|
||||
const isDarkColor = colorIsDark(color)
|
||||
const theme: Recordable = {
|
||||
// 左侧菜单边框颜色
|
||||
leftMenuBorderColor: isDarkColor ? 'inherit' : '#eee',
|
||||
// 左侧菜单背景颜色
|
||||
leftMenuBgColor: color,
|
||||
// 左侧菜单浅色背景颜色
|
||||
leftMenuBgLightColor: isDarkColor ? lighten(color!, 6) : color,
|
||||
// 左侧菜单选中背景颜色
|
||||
leftMenuBgActiveColor: isDarkColor
|
||||
? 'var(--el-color-primary)'
|
||||
: hexToRGB(unref(primaryColor), 0.1),
|
||||
// 左侧菜单收起选中背景颜色
|
||||
leftMenuCollapseBgActiveColor: isDarkColor
|
||||
? 'var(--el-color-primary)'
|
||||
: hexToRGB(unref(primaryColor), 0.1),
|
||||
// 左侧菜单字体颜色
|
||||
leftMenuTextColor: isDarkColor ? '#bfcbd9' : '#333',
|
||||
// 左侧菜单选中字体颜色
|
||||
leftMenuTextActiveColor: isDarkColor ? '#fff' : 'var(--el-color-primary)',
|
||||
// logo字体颜色
|
||||
logoTitleTextColor: isDarkColor ? '#fff' : 'inherit',
|
||||
// logo边框颜色
|
||||
logoBorderColor: isDarkColor ? color : '#eee'
|
||||
}
|
||||
this.setTheme(theme)
|
||||
this.setCssVarTheme()
|
||||
},
|
||||
setHeaderTheme(color: string) {
|
||||
const isDarkColor = colorIsDark(color)
|
||||
const textColor = isDarkColor ? '#fff' : 'inherit'
|
||||
const textHoverColor = isDarkColor ? lighten(color!, 6) : '#f6f6f6'
|
||||
const topToolBorderColor = isDarkColor ? color : '#eee'
|
||||
setCssVar('--top-header-bg-color', color)
|
||||
setCssVar('--top-header-text-color', textColor)
|
||||
setCssVar('--top-header-hover-color', textHoverColor)
|
||||
this.setTheme({
|
||||
topHeaderBgColor: color,
|
||||
topHeaderTextColor: textColor,
|
||||
topHeaderHoverColor: textHoverColor,
|
||||
topToolBorderColor
|
||||
})
|
||||
if (this.getLayout === 'top') {
|
||||
this.setMenuTheme(color)
|
||||
}
|
||||
},
|
||||
initTheme() {
|
||||
const isDark = useDark({
|
||||
valueDark: 'dark',
|
||||
valueLight: 'light'
|
||||
})
|
||||
isDark.value = this.getIsDark
|
||||
}
|
||||
}
|
||||
},
|
||||
persist: true
|
||||
})
|
||||
|
||||
export const useAppStoreWithOut = () => {
|
||||
|
@ -2,7 +2,6 @@ import { defineStore } from 'pinia'
|
||||
import { store } from '../index'
|
||||
import { UserLoginType } from '@/api/login/types'
|
||||
import { loginApi } from '@/api/login'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
import { useStorage } from '@/hooks/web/useStorage'
|
||||
import { getCurrentAdminUserInfo } from '@/api/vadmin/auth/user'
|
||||
import { resetRouter } from '@/router'
|
||||
@ -10,7 +9,7 @@ import { useTagsViewStore } from '@/store/modules/tagsView'
|
||||
import router from '@/router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
const { setStorage, clear } = useStorage()
|
||||
const { clear } = useStorage()
|
||||
|
||||
export interface UserState {
|
||||
id?: number
|
||||
@ -32,6 +31,9 @@ export interface AuthState {
|
||||
isUser: boolean // 是否已经登录并获取到用户信息
|
||||
roles: string[] // 当前用户角色 role_key 列表
|
||||
permissions: string[] // 当前用户权限列表
|
||||
tokenKey: string // 提交认证请求时,设置的 header key
|
||||
token: string // 认证 token
|
||||
refreshToken: string // 刷新 token
|
||||
}
|
||||
|
||||
export const useAuthStore = defineStore('auth', {
|
||||
@ -40,10 +42,22 @@ export const useAuthStore = defineStore('auth', {
|
||||
user: {},
|
||||
roles: [],
|
||||
permissions: [],
|
||||
isUser: false
|
||||
isUser: false,
|
||||
tokenKey: 'Authorization',
|
||||
token: '',
|
||||
refreshToken: ''
|
||||
}
|
||||
},
|
||||
getters: {
|
||||
getTokenKey(): string {
|
||||
return this.tokenKey
|
||||
},
|
||||
getToken(): string {
|
||||
return this.token
|
||||
},
|
||||
getRefreshToken(): string {
|
||||
return this.refreshToken
|
||||
},
|
||||
getUser(): UserState {
|
||||
return this.user
|
||||
},
|
||||
@ -58,24 +72,34 @@ export const useAuthStore = defineStore('auth', {
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
setToken(token: string) {
|
||||
this.token = token
|
||||
},
|
||||
setRefreshToken(refreshToken: string) {
|
||||
this.refreshToken = refreshToken
|
||||
},
|
||||
async login(formData: UserLoginType) {
|
||||
formData.platform = '0'
|
||||
const res = await loginApi(formData)
|
||||
if (res) {
|
||||
const appStore = useAppStore()
|
||||
setStorage(appStore.getToken, `${res.data.token_type} ${res.data.access_token}`)
|
||||
setStorage(appStore.getRefreshToken, res.data.refresh_token)
|
||||
this.token = `${res.data.token_type} ${res.data.access_token}`
|
||||
this.refreshToken = res.data.refresh_token
|
||||
// 获取当前登录用户的信息
|
||||
await this.setUserInfo()
|
||||
}
|
||||
return res
|
||||
},
|
||||
logout(message?: string) {
|
||||
clear()
|
||||
reset() {
|
||||
this.user = {}
|
||||
this.roles = []
|
||||
this.permissions = []
|
||||
this.isUser = false
|
||||
this.token = ''
|
||||
this.refreshToken = ''
|
||||
},
|
||||
logout(message?: string) {
|
||||
clear()
|
||||
this.reset()
|
||||
const tagsViewStore = useTagsViewStore()
|
||||
tagsViewStore.delAllViews()
|
||||
resetRouter()
|
||||
@ -105,7 +129,8 @@ export const useAuthStore = defineStore('auth', {
|
||||
})
|
||||
this.permissions = res.data.permissions
|
||||
}
|
||||
}
|
||||
},
|
||||
persist: true
|
||||
})
|
||||
|
||||
export const useAuthStoreWithOut = () => {
|
||||
|
@ -10,7 +10,11 @@ export const useDictStore = defineStore('dict', {
|
||||
state: (): DictState => ({
|
||||
dictObj: {}
|
||||
}),
|
||||
getters: {},
|
||||
getters: {
|
||||
getDictObjData(): Recordable {
|
||||
return this.dictObj
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
async getDictObj(dictTypes: string[]) {
|
||||
const result: Recordable = {}
|
||||
@ -34,7 +38,8 @@ export const useDictStore = defineStore('dict', {
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
},
|
||||
persist: true
|
||||
})
|
||||
|
||||
export const useDictStoreWithOut = () => {
|
||||
|
@ -5,7 +5,7 @@ import en from 'element-plus/es/locale/lang/en'
|
||||
import { useStorage } from '@/hooks/web/useStorage'
|
||||
import { LocaleDropdownType } from '@/components/LocaleDropdown'
|
||||
|
||||
const { getStorage, setStorage } = useStorage()
|
||||
const { getStorage, setStorage } = useStorage('localStorage')
|
||||
|
||||
const elLocaleMap = {
|
||||
'zh-CN': zhCn,
|
||||
@ -51,7 +51,8 @@ export const useLocaleStore = defineStore('locales', {
|
||||
this.currentLocale.elLocale = elLocaleMap[localeMap?.lang]
|
||||
setStorage('lang', localeMap?.lang)
|
||||
}
|
||||
}
|
||||
},
|
||||
persist: true
|
||||
})
|
||||
|
||||
export const useLocaleStoreWithOut = () => {
|
||||
|
@ -40,10 +40,7 @@ export const useLockStore = defineStore('lock', {
|
||||
}
|
||||
}
|
||||
},
|
||||
persist: {
|
||||
enabled: true,
|
||||
strategies: [{ key: 'lock', storage: localStorage }]
|
||||
}
|
||||
persist: true
|
||||
})
|
||||
|
||||
export const useLockStoreWithOut = () => {
|
||||
|
@ -60,6 +60,9 @@ export const usePermissionStore = defineStore('permission', {
|
||||
setMenuTabRouters(routers: AppRouteRecordRaw[]): void {
|
||||
this.menuTabRouters = routers
|
||||
}
|
||||
},
|
||||
persist: {
|
||||
paths: ['routers', 'addRouters', 'menuTabRouters']
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -4,12 +4,7 @@ import { getRawRoute } from '@/utils/routerHelper'
|
||||
import { defineStore } from 'pinia'
|
||||
import { store } from '../index'
|
||||
import { findIndex } from '@/utils'
|
||||
import { useStorage } from '@/hooks/web/useStorage'
|
||||
import { useAppStoreWithOut } from './app'
|
||||
|
||||
const appStore = useAppStoreWithOut()
|
||||
|
||||
const { getStorage } = useStorage()
|
||||
import { useAuthStoreWithOut } from './auth'
|
||||
|
||||
export interface TagsViewState {
|
||||
visitedViews: RouteLocationNormalizedLoaded[]
|
||||
@ -95,8 +90,9 @@ export const useTagsViewStore = defineStore('tagsView', {
|
||||
},
|
||||
// 删除所有tag
|
||||
delAllVisitedViews() {
|
||||
const authStore = useAuthStoreWithOut()
|
||||
// const affixTags = this.visitedViews.filter((tag) => tag.meta.affix)
|
||||
this.visitedViews = getStorage(appStore.getUserInfo)
|
||||
this.visitedViews = authStore.getUser
|
||||
? this.visitedViews.filter((tag) => tag?.meta?.affix)
|
||||
: []
|
||||
},
|
||||
@ -157,7 +153,8 @@ export const useTagsViewStore = defineStore('tagsView', {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
persist: false
|
||||
})
|
||||
|
||||
export const useTagsViewStoreWithOut = () => {
|
||||
|
@ -151,3 +151,22 @@ const subtractLight = (color: string, amount: number) => {
|
||||
const c = cc < 0 ? 0 : cc
|
||||
return c.toString(16).length > 1 ? c.toString(16) : `0${c.toString(16)}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Mixes two colors.
|
||||
*
|
||||
* @param {string} color1 - The first color, should be a 6-digit hexadecimal color code starting with `#`.
|
||||
* @param {string} color2 - The second color, should be a 6-digit hexadecimal color code starting with `#`.
|
||||
* @param {number} [weight=0.5] - The weight of color1 in the mix, should be a number between 0 and 1, where 0 represents 100% of color2, and 1 represents 100% of color1.
|
||||
* @returns {string} The mixed color, a 6-digit hexadecimal color code starting with `#`.
|
||||
*/
|
||||
export const mix = (color1: string, color2: string, weight: number = 0.5): string => {
|
||||
let color = '#'
|
||||
for (let i = 0; i <= 2; i++) {
|
||||
const c1 = parseInt(color1.substring(1 + i * 2, 3 + i * 2), 16)
|
||||
const c2 = parseInt(color2.substring(1 + i * 2, 3 + i * 2), 16)
|
||||
const c = Math.round(c1 * weight + c2 * (1 - weight))
|
||||
color += c.toString(16).padStart(2, '0')
|
||||
}
|
||||
return color
|
||||
}
|
||||
|
@ -1,5 +1,3 @@
|
||||
// import type { Plugin } from 'vue'
|
||||
|
||||
/**
|
||||
*
|
||||
* @param component 需要注册的组件
|
||||
@ -47,6 +45,10 @@ export const setCssVar = (prop: string, val: any, dom = document.documentElement
|
||||
dom.style.setProperty(prop, val)
|
||||
}
|
||||
|
||||
export const getCssVar = (prop: string, dom = document.documentElement) => {
|
||||
return getComputedStyle(dom).getPropertyValue(prop)
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找数组对象的某个下标
|
||||
* @param {Array} ary 查找的数组
|
||||
@ -123,6 +125,17 @@ export function firstUpperCase(str: string) {
|
||||
return str.toLowerCase().replace(/( |^)[a-z]/g, (L) => L.toUpperCase())
|
||||
}
|
||||
|
||||
/**
|
||||
* 把对象转为formData
|
||||
*/
|
||||
export function objToFormData(obj: Recordable) {
|
||||
const formData = new FormData()
|
||||
Object.keys(obj).forEach((key) => {
|
||||
formData.append(key, obj[key])
|
||||
})
|
||||
return formData
|
||||
}
|
||||
|
||||
// 根据当前时间获取祝福语
|
||||
export const getGreeting = (): string => {
|
||||
const now = new Date()
|
||||
|
@ -1,12 +1,11 @@
|
||||
import { createTypes, VueTypesInterface, VueTypeValidableDef } from 'vue-types'
|
||||
import { VueTypeValidableDef, VueTypesInterface, createTypes, toValidableType } from 'vue-types'
|
||||
import { CSSProperties } from 'vue'
|
||||
|
||||
// 自定义扩展vue-types
|
||||
type PropTypes = VueTypesInterface & {
|
||||
readonly style: VueTypeValidableDef<CSSProperties>
|
||||
}
|
||||
|
||||
const propTypes = createTypes({
|
||||
const newPropTypes = createTypes({
|
||||
func: undefined,
|
||||
bool: undefined,
|
||||
string: undefined,
|
||||
@ -15,15 +14,12 @@ const propTypes = createTypes({
|
||||
integer: undefined
|
||||
}) as PropTypes
|
||||
|
||||
// 需要自定义扩展的类型
|
||||
// see: https://dwightjack.github.io/vue-types/advanced/extending-vue-types.html#the-extend-method
|
||||
propTypes.extend([
|
||||
{
|
||||
name: 'style',
|
||||
getter: true,
|
||||
type: [String, Object],
|
||||
default: undefined
|
||||
class propTypes extends newPropTypes {
|
||||
static get style() {
|
||||
return toValidableType('style', {
|
||||
type: [String, Object]
|
||||
})
|
||||
}
|
||||
])
|
||||
}
|
||||
|
||||
export { propTypes }
|
||||
|
@ -155,12 +155,12 @@ initMap()
|
||||
background-color: #f13737;
|
||||
box-shadow: 0px 0px 15px #f61212;
|
||||
border-radius: 50%;
|
||||
-webkit-animation-name: 'alarmDeviceBreath'; /*动画属性名,也就是我们前面keyframes定义的动画名*/
|
||||
-webkit-animation-duration: 1s; /*动画持续时间*/
|
||||
-webkit-animation-timing-function: ease; /*动画频率,和transition-timing-function是一样的*/
|
||||
-webkit-animation-delay: 0s; /*动画延迟时间*/
|
||||
-webkit-animation-iteration-count: infinite; /*定义循环资料,infinite为无限次*/
|
||||
-webkit-animation-direction: alternate; /*定义动画方式*/
|
||||
--webkit-animation-name: 'alarmDeviceBreath'; /*动画属性名,也就是我们前面keyframes定义的动画名*/
|
||||
--webkit-animation-duration: 1s; /*动画持续时间*/
|
||||
--webkit-animation-timing-function: ease; /*动画频率,和transition-timing-function是一样的*/
|
||||
--webkit-animation-delay: 0s; /*动画延迟时间*/
|
||||
--webkit-animation-iteration-count: infinite; /*定义循环资料,infinite为无限次*/
|
||||
--webkit-animation-direction: alternate; /*定义动画方式*/
|
||||
}
|
||||
</style>
|
||||
|
||||
|
@ -3,14 +3,14 @@ import { ElCard, ElRow, ElCol, ElTabs, ElTabPane, ElAvatar } from 'element-plus'
|
||||
import { computed, ref } from 'vue'
|
||||
import InfoWrite from './components/InfoWrite.vue'
|
||||
import PasswordWrite from './components/PasswordWrite.vue'
|
||||
import { useAuthStoreWithOut } from '@/store/modules/auth'
|
||||
import { useAuthStore } from '@/store/modules/auth'
|
||||
import avatar from '@/assets/imgs/avatar.jpg'
|
||||
import { selectDictLabel, DictDetail } from '@/utils/dict'
|
||||
import { useDictStore } from '@/store/modules/dict'
|
||||
|
||||
const activeName = ref('info')
|
||||
|
||||
const authStore = useAuthStoreWithOut()
|
||||
const authStore = useAuthStore()
|
||||
|
||||
let genderOptions = ref<DictDetail[]>([])
|
||||
|
||||
|
@ -3,13 +3,14 @@ import { Form, FormSchema } from '@/components/Form'
|
||||
import { useForm } from '@/hooks/web/useForm'
|
||||
import { reactive, ref } from 'vue'
|
||||
import { useValidator } from '@/hooks/web/useValidator'
|
||||
import { useAuthStoreWithOut } from '@/store/modules/auth'
|
||||
import { ElButton, ElMessage } from 'element-plus'
|
||||
import { useAuthStore } from '@/store/modules/auth'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { postCurrentUserUpdateInfo } from '@/api/vadmin/auth/user'
|
||||
import { BaseButton } from '@/components/Button'
|
||||
|
||||
const { required, isTelephone } = useValidator()
|
||||
|
||||
const authStore = useAuthStoreWithOut()
|
||||
const authStore = useAuthStore()
|
||||
|
||||
const formSchema = reactive<FormSchema[]>([
|
||||
{
|
||||
@ -92,9 +93,9 @@ const formSchema = reactive<FormSchema[]>([
|
||||
return (
|
||||
<>
|
||||
<div class="w-[50%]">
|
||||
<ElButton loading={loading.value} type="primary" class="w-[100%]" onClick={save}>
|
||||
<BaseButton loading={loading.value} type="primary" class="w-[100%]" onClick={save}>
|
||||
保存
|
||||
</ElButton>
|
||||
</BaseButton>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
@ -3,13 +3,14 @@ import { Form, FormSchema } from '@/components/Form'
|
||||
import { useForm } from '@/hooks/web/useForm'
|
||||
import { reactive, ref } from 'vue'
|
||||
import { useValidator } from '@/hooks/web/useValidator'
|
||||
import { useAuthStoreWithOut } from '@/store/modules/auth'
|
||||
import { ElButton, ElMessage } from 'element-plus'
|
||||
import { useAuthStore } from '@/store/modules/auth'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { postCurrentUserResetPassword } from '@/api/vadmin/auth/user'
|
||||
import { BaseButton } from '@/components/Button'
|
||||
|
||||
const { required } = useValidator()
|
||||
|
||||
const authStore = useAuthStoreWithOut()
|
||||
const authStore = useAuthStore()
|
||||
|
||||
const formSchema = reactive<FormSchema[]>([
|
||||
{
|
||||
@ -57,9 +58,9 @@ const formSchema = reactive<FormSchema[]>([
|
||||
return (
|
||||
<>
|
||||
<div class="w-[50%]">
|
||||
<ElButton loading={loading.value} type="primary" class="w-[100%]" onClick={save}>
|
||||
<BaseButton loading={loading.value} type="primary" class="w-[100%]" onClick={save}>
|
||||
保存
|
||||
</ElButton>
|
||||
</BaseButton>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
@ -2,18 +2,18 @@
|
||||
import { reactive, ref, watch } from 'vue'
|
||||
import { Form } from '@/components/Form'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { ElButton, ElCheckbox, ElLink } from 'element-plus'
|
||||
import { ElCheckbox, ElLink } from 'element-plus'
|
||||
import { useForm } from '@/hooks/web/useForm'
|
||||
import { getRoleMenusApi } from '@/api/login'
|
||||
import { useAuthStoreWithOut } from '@/store/modules/auth'
|
||||
import { useAuthStore } from '@/store/modules/auth'
|
||||
import { usePermissionStore } from '@/store/modules/permission'
|
||||
import { useRouter } from 'vue-router'
|
||||
import type { RouteLocationNormalizedLoaded, RouteRecordRaw } from 'vue-router'
|
||||
import { UserLoginType } from '@/api/login/types'
|
||||
import { useValidator } from '@/hooks/web/useValidator'
|
||||
import { useStorage } from '@/hooks/web/useStorage'
|
||||
import { FormSchema } from '@/components/Form'
|
||||
import { Icon } from '@/components/Icon'
|
||||
import { BaseButton } from '@/components/Button'
|
||||
|
||||
const emit = defineEmits(['to-telephone'])
|
||||
|
||||
@ -21,10 +21,9 @@ const { required } = useValidator()
|
||||
|
||||
const permissionStore = usePermissionStore()
|
||||
|
||||
const authStore = useAuthStoreWithOut()
|
||||
const authStore = useAuthStore()
|
||||
|
||||
const { currentRoute, addRoute, push } = useRouter()
|
||||
const { setStorage } = useStorage()
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
@ -122,14 +121,19 @@ const schema = reactive<FormSchema[]>([
|
||||
return (
|
||||
<>
|
||||
<div class="w-[100%]">
|
||||
<ElButton loading={loading.value} type="primary" class="w-[100%]" onClick={signIn}>
|
||||
<BaseButton
|
||||
loading={loading.value}
|
||||
type="primary"
|
||||
class="w-[100%]"
|
||||
onClick={signIn}
|
||||
>
|
||||
{t('login.login')}
|
||||
</ElButton>
|
||||
</BaseButton>
|
||||
</div>
|
||||
<div class="w-[100%] mt-15px">
|
||||
<ElButton class="w-[100%]" onClick={toTelephoneLogin}>
|
||||
<BaseButton class="w-[100%]" onClick={toTelephoneLogin}>
|
||||
{t('login.smsLogin')}
|
||||
</ElButton>
|
||||
</BaseButton>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
@ -241,7 +245,6 @@ const getMenu = async () => {
|
||||
const res = await getRoleMenusApi()
|
||||
if (res) {
|
||||
const routers = res.data || []
|
||||
setStorage('roleRouters', routers)
|
||||
await permissionStore.generateRoutes(routers).catch(() => {})
|
||||
permissionStore.getAddRouters.forEach((route) => {
|
||||
addRoute(route as RouteRecordRaw) // 动态添加可访问路由表
|
||||
|
@ -3,16 +3,16 @@ import { Form } from '@/components/Form'
|
||||
import { reactive, ref, watch } from 'vue'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { useForm } from '@/hooks/web/useForm'
|
||||
import { ElButton, ElInput, FormRules, ElDivider, ElMessage } from 'element-plus'
|
||||
import { ElInput, FormRules, ElDivider, ElMessage } from 'element-plus'
|
||||
import { useValidator } from '@/hooks/web/useValidator'
|
||||
import { FormSchema } from '@/components/Form'
|
||||
import { postSMSCodeApi } from '@/api/login'
|
||||
import { UserLoginType } from '@/api/login/types'
|
||||
import { useAuthStoreWithOut } from '@/store/modules/auth'
|
||||
import { useAuthStore } from '@/store/modules/auth'
|
||||
import { RouteLocationNormalizedLoaded, useRouter, RouteRecordRaw } from 'vue-router'
|
||||
import { getRoleMenusApi } from '@/api/login'
|
||||
import { useStorage } from '@/hooks/web/useStorage'
|
||||
import { usePermissionStore } from '@/store/modules/permission'
|
||||
import { BaseButton } from '@/components/Button'
|
||||
|
||||
const emit = defineEmits(['to-password'])
|
||||
|
||||
@ -22,8 +22,7 @@ const { t } = useI18n()
|
||||
const { required } = useValidator()
|
||||
const { currentRoute, addRoute, push } = useRouter()
|
||||
const permissionStore = usePermissionStore()
|
||||
const authStore = useAuthStoreWithOut()
|
||||
const { setStorage } = useStorage()
|
||||
const authStore = useAuthStore()
|
||||
|
||||
const schema = reactive<FormSchema[]>([
|
||||
{
|
||||
@ -74,13 +73,13 @@ const schema = reactive<FormSchema[]>([
|
||||
<>
|
||||
<ElDivider direction="vertical" />
|
||||
{SMSCodeStatus.value ? (
|
||||
<ElButton type="primary" link onClick={getSMSCode}>
|
||||
<BaseButton type="primary" link onClick={getSMSCode}>
|
||||
{t('login.getSMSCode')}
|
||||
</ElButton>
|
||||
</BaseButton>
|
||||
) : (
|
||||
<ElButton type="primary" disabled={!SMSCodeStatus.value} link>
|
||||
<BaseButton type="primary" disabled={!SMSCodeStatus.value} link>
|
||||
{SMSCodeNumber.value + t('login.SMSCodeRetry')}
|
||||
</ElButton>
|
||||
</BaseButton>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
@ -110,19 +109,19 @@ const schema = reactive<FormSchema[]>([
|
||||
return (
|
||||
<div class="w-[100%]">
|
||||
<div class="w-[100%]">
|
||||
<ElButton
|
||||
<BaseButton
|
||||
type="primary"
|
||||
class="w-[100%]"
|
||||
loading={loading.value}
|
||||
onClick={telephoneCodeLogin}
|
||||
>
|
||||
{t('login.login')}
|
||||
</ElButton>
|
||||
</BaseButton>
|
||||
</div>
|
||||
<div class="w-[100%] mt-15px">
|
||||
<ElButton class="w-[100%]" onClick={toPasswordLogin}>
|
||||
<BaseButton class="w-[100%]" onClick={toPasswordLogin}>
|
||||
{t('login.passwordLogin')}
|
||||
</ElButton>
|
||||
</BaseButton>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
@ -216,7 +215,6 @@ const getMenu = async () => {
|
||||
const res = await getRoleMenusApi()
|
||||
if (res) {
|
||||
const routers = res.data || []
|
||||
setStorage('roleRouters', routers)
|
||||
await permissionStore.generateRoutes(routers).catch(() => {})
|
||||
permissionStore.getAddRouters.forEach((route) => {
|
||||
addRoute(route as RouteRecordRaw) // 动态添加可访问路由表
|
||||
|
@ -3,21 +3,19 @@ import { Form, FormSchema } from '@/components/Form'
|
||||
import { useForm } from '@/hooks/web/useForm'
|
||||
import { computed, reactive, ref, watch } from 'vue'
|
||||
import { useValidator } from '@/hooks/web/useValidator'
|
||||
import { useAuthStoreWithOut } from '@/store/modules/auth'
|
||||
import { ElButton, ElMessage } from 'element-plus'
|
||||
import { useAuthStore } from '@/store/modules/auth'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { postCurrentUserResetPassword } from '@/api/vadmin/auth/user'
|
||||
import { getRoleMenusApi } from '@/api/login'
|
||||
import { useStorage } from '@/hooks/web/useStorage'
|
||||
import { usePermissionStore } from '@/store/modules/permission'
|
||||
import { RouteLocationNormalizedLoaded, RouteRecordRaw, useRouter } from 'vue-router'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
import { Footer } from '@/components/Footer'
|
||||
|
||||
const { required } = useValidator()
|
||||
const { setStorage } = useStorage()
|
||||
const { addRoute, push, currentRoute } = useRouter()
|
||||
|
||||
const authStore = useAuthStoreWithOut()
|
||||
const authStore = useAuthStore()
|
||||
const appStore = useAppStore()
|
||||
const permissionStore = usePermissionStore()
|
||||
|
||||
@ -112,7 +110,6 @@ const getMenu = async () => {
|
||||
const res = await getRoleMenusApi()
|
||||
if (res) {
|
||||
const routers = res.data || []
|
||||
setStorage('roleRouters', routers)
|
||||
await permissionStore.generateRoutes(routers).catch(() => {})
|
||||
permissionStore.getAddRouters.forEach((route) => {
|
||||
addRoute(route as RouteRecordRaw) // 动态添加可访问路由表
|
||||
@ -137,9 +134,9 @@ const getMenu = async () => {
|
||||
class="dark:(border-1 border-[var(--el-border-color)] border-solid)"
|
||||
/>
|
||||
<div class="w-[100%]">
|
||||
<ElButton :loading="loading" type="primary" class="w-[100%]" @click="save">
|
||||
<BaseButton :loading="loading" type="primary" class="w-[100%]" @click="save">
|
||||
重置密码
|
||||
</ElButton>
|
||||
</BaseButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -9,10 +9,11 @@ import {
|
||||
import { useTable } from '@/hooks/web/useTable'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { Table, TableColumn } from '@/components/Table'
|
||||
import { ElButton, ElSwitch, ElRow, ElCol } from 'element-plus'
|
||||
import { ElSwitch, ElRow, ElCol } from 'element-plus'
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
import Write from './components/Write.vue'
|
||||
import { Dialog } from '@/components/Dialog'
|
||||
import { BaseButton } from '@/components/Button'
|
||||
|
||||
defineOptions({
|
||||
name: 'AuthDept'
|
||||
@ -89,7 +90,7 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
const row = data.row
|
||||
return (
|
||||
<>
|
||||
<ElSwitch value={!row.disabled} disabled />
|
||||
<ElSwitch modelValue={!row.disabled} disabled />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -105,13 +106,13 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
const row = data.row
|
||||
return (
|
||||
<>
|
||||
<ElButton type="primary" link size="small" onClick={() => editAction(row)}>
|
||||
<BaseButton type="primary" link size="small" onClick={() => editAction(row)}>
|
||||
编辑
|
||||
</ElButton>
|
||||
<ElButton type="primary" link size="small" onClick={() => addSonAction(row)}>
|
||||
</BaseButton>
|
||||
<BaseButton type="primary" link size="small" onClick={() => addSonAction(row)}>
|
||||
添加子部门
|
||||
</ElButton>
|
||||
<ElButton
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
type="danger"
|
||||
loading={delLoading.value}
|
||||
link
|
||||
@ -119,7 +120,7 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
onClick={() => delData(row)}
|
||||
>
|
||||
删除
|
||||
</ElButton>
|
||||
</BaseButton>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -212,7 +213,7 @@ const save = async () => {
|
||||
<template #toolbar>
|
||||
<ElRow :gutter="10">
|
||||
<ElCol :span="1.5">
|
||||
<ElButton type="primary" @click="addAction">新增部门</ElButton>
|
||||
<BaseButton type="primary" @click="addAction">新增部门</BaseButton>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</template>
|
||||
@ -223,10 +224,15 @@ const save = async () => {
|
||||
<Write ref="writeRef" :current-row="currentRow" :parent-id="parentId" />
|
||||
|
||||
<template #footer>
|
||||
<ElButton v-if="actionType !== 'detail'" type="primary" :loading="saveLoading" @click="save">
|
||||
<BaseButton
|
||||
v-if="actionType !== 'detail'"
|
||||
type="primary"
|
||||
:loading="saveLoading"
|
||||
@click="save"
|
||||
>
|
||||
{{ t('exampleDemo.save') }}
|
||||
</ElButton>
|
||||
<ElButton @click="dialogVisible = false">{{ t('dialogDemo.close') }}</ElButton>
|
||||
</BaseButton>
|
||||
<BaseButton @click="dialogVisible = false">{{ t('dialogDemo.close') }}</BaseButton>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
@ -9,13 +9,14 @@ import {
|
||||
import { useTable } from '@/hooks/web/useTable'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { Table, TableColumn } from '@/components/Table'
|
||||
import { ElButton, ElSwitch, ElRow, ElCol } from 'element-plus'
|
||||
import { ElSwitch, ElRow, ElCol } from 'element-plus'
|
||||
import { Icon } from '@/components/Icon'
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
import Write from './components/Write.vue'
|
||||
import { Dialog } from '@/components/Dialog'
|
||||
import { useDictStore } from '@/store/modules/dict'
|
||||
import { selectDictLabel, DictDetail } from '@/utils/dict'
|
||||
import { BaseButton } from '@/components/Button'
|
||||
|
||||
defineOptions({
|
||||
name: 'AuthMenu'
|
||||
@ -122,7 +123,7 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
const row = data.row
|
||||
return (
|
||||
<>
|
||||
<ElSwitch value={!row.noCache} disabled />
|
||||
<ElSwitch modelValue={!row.noCache} disabled />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -138,7 +139,7 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
const row = data.row
|
||||
return (
|
||||
<>
|
||||
<ElSwitch value={!row.hidden} disabled />
|
||||
<ElSwitch modelValue={!row.hidden} disabled />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -154,7 +155,7 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
const row = data.row
|
||||
return (
|
||||
<>
|
||||
<ElSwitch value={!row.disabled} disabled />
|
||||
<ElSwitch modelValue={!row.disabled} disabled />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -173,7 +174,7 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
const del = ['auth.menu.delete']
|
||||
return (
|
||||
<>
|
||||
<ElButton
|
||||
<BaseButton
|
||||
type="primary"
|
||||
v-hasPermi={update}
|
||||
link
|
||||
@ -181,8 +182,8 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
onClick={() => editAction(row)}
|
||||
>
|
||||
编辑
|
||||
</ElButton>
|
||||
<ElButton
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
type="primary"
|
||||
v-hasPermi={add}
|
||||
link
|
||||
@ -190,8 +191,8 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
onClick={() => addSonAction(row)}
|
||||
>
|
||||
添加子菜单
|
||||
</ElButton>
|
||||
<ElButton
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
type="danger"
|
||||
v-hasPermi={del}
|
||||
loading={delLoading.value}
|
||||
@ -200,7 +201,7 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
onClick={() => delData(row)}
|
||||
>
|
||||
删除
|
||||
</ElButton>
|
||||
</BaseButton>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -293,8 +294,8 @@ const save = async () => {
|
||||
<template #toolbar>
|
||||
<ElRow :gutter="10">
|
||||
<ElCol :span="1.5">
|
||||
<ElButton type="primary" v-hasPermi="['auth.menu.create']" @click="addAction"
|
||||
>新增菜单</ElButton
|
||||
<BaseButton type="primary" v-hasPermi="['auth.menu.create']" @click="addAction"
|
||||
>新增菜单</BaseButton
|
||||
>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
@ -306,10 +307,15 @@ const save = async () => {
|
||||
<Write ref="writeRef" :current-row="currentRow" :parent-id="parentId" />
|
||||
|
||||
<template #footer>
|
||||
<ElButton v-if="actionType !== 'detail'" type="primary" :loading="saveLoading" @click="save">
|
||||
<BaseButton
|
||||
v-if="actionType !== 'detail'"
|
||||
type="primary"
|
||||
:loading="saveLoading"
|
||||
@click="save"
|
||||
>
|
||||
{{ t('exampleDemo.save') }}
|
||||
</ElButton>
|
||||
<ElButton @click="dialogVisible = false">{{ t('dialogDemo.close') }}</ElButton>
|
||||
</BaseButton>
|
||||
<BaseButton @click="dialogVisible = false">{{ t('dialogDemo.close') }}</BaseButton>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
@ -5,8 +5,8 @@ import { PropType, reactive, watch } from 'vue'
|
||||
import { useValidator } from '@/hooks/web/useValidator'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { getMenuTreeOptionsApi } from '@/api/vadmin/auth/menu'
|
||||
import { ElButton } from 'element-plus'
|
||||
import { IconPicker } from '@/components/IconPicker'
|
||||
import { BaseButton } from '@/components/Button'
|
||||
|
||||
const { required } = useValidator()
|
||||
|
||||
@ -88,9 +88,9 @@ const formSchema = reactive<FormSchema[]>([
|
||||
<div style="display: flex; justify-content: space-between">
|
||||
<IconPicker style="width: 470px" input-disabled={false} v-model={data['icon']} />
|
||||
<div style="margin-left: 10px">
|
||||
<ElButton type="primary" onClick={toIconify}>
|
||||
<BaseButton type="primary" onClick={toIconify}>
|
||||
Iconify
|
||||
</ElButton>
|
||||
</BaseButton>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
import { useTable } from '@/hooks/web/useTable'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { Table, TableColumn } from '@/components/Table'
|
||||
import { ElButton, ElSwitch } from 'element-plus'
|
||||
import { ElSwitch } from 'element-plus'
|
||||
import { Search } from '@/components/Search'
|
||||
import { FormSchema } from '@/components/Form'
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
@ -19,6 +19,7 @@ import AuthManage from './components/AuthManage.vue'
|
||||
import { Dialog } from '@/components/Dialog'
|
||||
import { DictDetail, selectDictLabel } from '@/utils/dict'
|
||||
import { useDictStore } from '@/store/modules/dict'
|
||||
import { BaseButton } from '@/components/Button'
|
||||
|
||||
defineOptions({
|
||||
name: 'AuthRole'
|
||||
@ -105,7 +106,7 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
const row = data.row
|
||||
return (
|
||||
<>
|
||||
<ElSwitch value={!row.disabled} disabled />
|
||||
<ElSwitch modelValue={!row.disabled} disabled />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -120,7 +121,7 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
const row = data.row
|
||||
return (
|
||||
<>
|
||||
<ElSwitch value={row.is_admin} disabled />
|
||||
<ElSwitch modelValue={row.is_admin} disabled />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -143,7 +144,7 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
const del = ['auth.role.delete']
|
||||
return (
|
||||
<>
|
||||
<ElButton
|
||||
<BaseButton
|
||||
v-show={row.id !== 1}
|
||||
type="primary"
|
||||
v-hasPermi={update}
|
||||
@ -152,8 +153,8 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
onClick={() => editAction(row)}
|
||||
>
|
||||
编辑
|
||||
</ElButton>
|
||||
<ElButton
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
v-show={row.id !== 1}
|
||||
type="primary"
|
||||
link
|
||||
@ -161,8 +162,8 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
onClick={() => authManageActive(row)}
|
||||
>
|
||||
权限管理
|
||||
</ElButton>
|
||||
<ElButton
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
v-show={row.id !== 1}
|
||||
type="danger"
|
||||
v-hasPermi={del}
|
||||
@ -172,7 +173,7 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
onClick={() => delData(row)}
|
||||
>
|
||||
删除
|
||||
</ElButton>
|
||||
</BaseButton>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -329,8 +330,8 @@ const save = async () => {
|
||||
<template #toolbar>
|
||||
<ElRow :gutter="10">
|
||||
<ElCol :span="1.5">
|
||||
<ElButton type="primary" v-hasPermi="['auth.role.create']" @click="addAction"
|
||||
>新增角色</ElButton
|
||||
<BaseButton type="primary" v-hasPermi="['auth.role.create']" @click="addAction"
|
||||
>新增角色</BaseButton
|
||||
>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
@ -342,10 +343,10 @@ const save = async () => {
|
||||
<Write ref="writeRef" :current-row="currentRow" />
|
||||
|
||||
<template #footer>
|
||||
<ElButton type="primary" :loading="saveLoading" @click="save">
|
||||
<BaseButton type="primary" :loading="saveLoading" @click="save">
|
||||
{{ t('exampleDemo.save') }}
|
||||
</ElButton>
|
||||
<ElButton @click="dialogVisible = false">{{ t('dialogDemo.close') }}</ElButton>
|
||||
</BaseButton>
|
||||
<BaseButton @click="dialogVisible = false">{{ t('dialogDemo.close') }}</BaseButton>
|
||||
</template>
|
||||
</Dialog>
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
ElDrawer,
|
||||
ElButton,
|
||||
ElDivider,
|
||||
ElSelect,
|
||||
ElOption,
|
||||
@ -171,7 +170,7 @@ defineExpose({
|
||||
<div class="mt-1 text-[#909399]">
|
||||
<span>角色名称:{{ data.name }}</span>
|
||||
</div>
|
||||
<ElButton type="primary" :loading="loading" @click="submit">保存</ElButton>
|
||||
<BaseButton type="primary" :loading="loading" @click="submit">保存</BaseButton>
|
||||
</div>
|
||||
<ElDivider />
|
||||
<ElContainer>
|
||||
@ -192,7 +191,7 @@ defineExpose({
|
||||
/>
|
||||
</ElSelect>
|
||||
<div
|
||||
v-if="data.data_range === '4'"
|
||||
v-if="data.data_range === '3'"
|
||||
class="mt-3 max-h-[65vh] b-1 b-solid b-[#e5e7eb] p-10px overflow-auto"
|
||||
>
|
||||
<ElTree
|
||||
|
@ -11,7 +11,7 @@ import {
|
||||
import { useTable } from '@/hooks/web/useTable'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { Table, TableColumn } from '@/components/Table'
|
||||
import { ElButton, ElSwitch, ElRow, ElCol, ElMessage } from 'element-plus'
|
||||
import { ElSwitch, ElRow, ElCol, ElMessage } from 'element-plus'
|
||||
import { Search } from '@/components/Search'
|
||||
import { FormSchema } from '@/components/Form'
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
@ -22,6 +22,7 @@ import { useDictStore } from '@/store/modules/dict'
|
||||
import Import from './components/Import.vue'
|
||||
import PasswordSendSMS from './components/PasswordSendSMS.vue'
|
||||
import PasswordSendEmail from './components/PasswordSendEmail.vue'
|
||||
import { BaseButton } from '@/components/Button'
|
||||
|
||||
defineOptions({
|
||||
name: 'AuthUser'
|
||||
@ -164,7 +165,7 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
const row = data.row
|
||||
return (
|
||||
<>
|
||||
<ElSwitch value={row.is_active} disabled />
|
||||
<ElSwitch modelValue={row.is_active} disabled />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -179,7 +180,7 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
const row = data.row
|
||||
return (
|
||||
<>
|
||||
<ElSwitch value={row.is_staff} disabled />
|
||||
<ElSwitch modelValue={row.is_staff} disabled />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -209,7 +210,7 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
const del = ['auth.user.delete']
|
||||
return (
|
||||
<>
|
||||
<ElButton
|
||||
<BaseButton
|
||||
type="primary"
|
||||
v-hasPermi={update}
|
||||
link
|
||||
@ -217,8 +218,8 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
onClick={() => editAction(row)}
|
||||
>
|
||||
编辑
|
||||
</ElButton>
|
||||
<ElButton
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
type="danger"
|
||||
v-hasPermi={del}
|
||||
loading={delLoading.value}
|
||||
@ -227,7 +228,7 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
onClick={() => delData(row)}
|
||||
>
|
||||
删除
|
||||
</ElButton>
|
||||
</BaseButton>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -438,22 +439,22 @@ const save = async () => {
|
||||
<template #toolbar>
|
||||
<ElRow :gutter="10">
|
||||
<ElCol :span="1.5" v-hasPermi="['auth.user.create']">
|
||||
<ElButton type="primary" @click="addAction">新增用户</ElButton>
|
||||
<BaseButton type="primary" @click="addAction">新增用户</BaseButton>
|
||||
</ElCol>
|
||||
<ElCol :span="1.5" v-hasPermi="['auth.user.import']">
|
||||
<ElButton @click="importList">批量导入用户</ElButton>
|
||||
<BaseButton @click="importList">批量导入用户</BaseButton>
|
||||
</ElCol>
|
||||
<ElCol :span="1.5" v-hasPermi="['auth.user.export']">
|
||||
<ElButton @click="exportQueryList()">导出筛选用户</ElButton>
|
||||
<BaseButton @click="exportQueryList()">导出筛选用户</BaseButton>
|
||||
</ElCol>
|
||||
<ElCol :span="1.5" v-hasPermi="['auth.user.reset']">
|
||||
<ElButton @click="sendPasswordToSMS">重置密码通知短信</ElButton>
|
||||
<BaseButton @click="sendPasswordToSMS">重置密码通知短信</BaseButton>
|
||||
</ElCol>
|
||||
<ElCol :span="1.5" v-hasPermi="['auth.user.reset']">
|
||||
<ElButton @click="sendPasswordToEmail">重置密码通知邮件</ElButton>
|
||||
<BaseButton @click="sendPasswordToEmail">重置密码通知邮件</BaseButton>
|
||||
</ElCol>
|
||||
<ElCol :span="1.5" v-hasPermi="['auth.user.delete']">
|
||||
<ElButton type="danger" @click="delData(null)">批量删除</ElButton>
|
||||
<BaseButton type="danger" @click="delData(null)">批量删除</BaseButton>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</template>
|
||||
@ -487,10 +488,10 @@ const save = async () => {
|
||||
/>
|
||||
|
||||
<template #footer v-if="actionType === 'add' || actionType === 'edit'">
|
||||
<ElButton type="primary" :loading="saveLoading" @click="save">
|
||||
<BaseButton type="primary" :loading="saveLoading" @click="save">
|
||||
{{ t('exampleDemo.save') }}
|
||||
</ElButton>
|
||||
<ElButton @click="dialogVisible = false">{{ t('dialogDemo.close') }}</ElButton>
|
||||
</BaseButton>
|
||||
<BaseButton @click="dialogVisible = false">{{ t('dialogDemo.close') }}</BaseButton>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
@ -3,7 +3,6 @@ import {
|
||||
ElLink,
|
||||
ElRow,
|
||||
ElCol,
|
||||
ElButton,
|
||||
ElTable,
|
||||
ElTableColumn,
|
||||
ElUpload,
|
||||
@ -130,21 +129,21 @@ const downloadErrorFile = async (row: Recordable) => {
|
||||
:disabled="tableData.length > 0"
|
||||
>
|
||||
<ElTooltip effect="dark" content="只支持上传XLSX文件" placement="top">
|
||||
<ElButton type="primary" size="small" :disabled="tableData.length > 0"
|
||||
>上传文件</ElButton
|
||||
<BaseButton type="primary" size="small" :disabled="tableData.length > 0"
|
||||
>上传文件</BaseButton
|
||||
>
|
||||
</ElTooltip>
|
||||
</ElUpload>
|
||||
</div>
|
||||
</ElCol>
|
||||
<ElCol :span="1.5">
|
||||
<ElButton
|
||||
<BaseButton
|
||||
type="primary"
|
||||
size="small"
|
||||
:disabled="tableData.length === 0"
|
||||
:loading="importLoading"
|
||||
@click="handleImport"
|
||||
>确认导入</ElButton
|
||||
>确认导入</BaseButton
|
||||
>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
@ -156,7 +155,7 @@ const downloadErrorFile = async (row: Recordable) => {
|
||||
<template #default>
|
||||
<ElPopconfirm title="确认删除吗?" @confirm="handleDelete">
|
||||
<template #reference>
|
||||
<ElButton link type="primary" size="small">删除</ElButton>
|
||||
<BaseButton link type="primary" size="small">删除</BaseButton>
|
||||
</template>
|
||||
</ElPopconfirm>
|
||||
</template>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { ElButton, ElTable, ElTableColumn, ElPopconfirm, ElMessage, ElTag } from 'element-plus'
|
||||
import { ElTable, ElTableColumn, ElPopconfirm, ElMessage, ElTag } from 'element-plus'
|
||||
import { postUsersInitPasswordSendEmailApi } from '@/api/vadmin/auth/user'
|
||||
import { ref, PropType } from 'vue'
|
||||
|
||||
@ -40,12 +40,12 @@ const initPassword = async () => {
|
||||
<div>
|
||||
<div class="flex justify-between">
|
||||
<span>已选用户列表</span>
|
||||
<ElButton
|
||||
<BaseButton
|
||||
type="primary"
|
||||
:disabled="tableData?.length === 0"
|
||||
:loading="loading"
|
||||
@click="initPassword"
|
||||
>确认重置并发送邮件通知</ElButton
|
||||
>确认重置并发送邮件通知</BaseButton
|
||||
>
|
||||
</div>
|
||||
<ElTable
|
||||
@ -86,8 +86,8 @@ const initPassword = async () => {
|
||||
<template #default="scope">
|
||||
<ElPopconfirm title="确认移除吗?" @confirm="handleDelete(scope.$index)">
|
||||
<template #reference>
|
||||
<ElButton v-if="scope.row.send_sms_status !== true" link type="primary" size="small"
|
||||
>移除</ElButton
|
||||
<BaseButton v-if="scope.row.send_sms_status !== true" link type="primary" size="small"
|
||||
>移除</BaseButton
|
||||
>
|
||||
</template>
|
||||
</ElPopconfirm>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { ElButton, ElTable, ElTableColumn, ElPopconfirm, ElMessage, ElTag } from 'element-plus'
|
||||
import { ElTable, ElTableColumn, ElPopconfirm, ElMessage, ElTag } from 'element-plus'
|
||||
import { postUsersInitPasswordSendSMSApi } from '@/api/vadmin/auth/user'
|
||||
import { ref, PropType } from 'vue'
|
||||
|
||||
@ -40,12 +40,12 @@ const initPassword = async () => {
|
||||
<div>
|
||||
<div class="flex justify-between">
|
||||
<span>已选用户列表</span>
|
||||
<ElButton
|
||||
<BaseButton
|
||||
type="primary"
|
||||
:disabled="tableData?.length === 0"
|
||||
:loading="loading"
|
||||
@click="initPassword"
|
||||
>确认重置并发送短信通知</ElButton
|
||||
>确认重置并发送短信通知</BaseButton
|
||||
>
|
||||
</div>
|
||||
<ElTable
|
||||
@ -86,8 +86,8 @@ const initPassword = async () => {
|
||||
<template #default="scope">
|
||||
<ElPopconfirm title="确认移除吗?" @confirm="handleDelete(scope.$index)">
|
||||
<template #reference>
|
||||
<ElButton v-if="scope.row.send_sms_status !== true" link type="primary" size="small"
|
||||
>移除</ElButton
|
||||
<BaseButton v-if="scope.row.send_sms_status !== true" link type="primary" size="small"
|
||||
>移除</BaseButton
|
||||
>
|
||||
</template>
|
||||
</ElPopconfirm>
|
||||
|
@ -3,13 +3,14 @@ import { reactive, ref, unref } from 'vue'
|
||||
import { getIssueListApi, delIssueListApi } from '@/api/vadmin/help/issue'
|
||||
import { useTable } from '@/hooks/web/useTable'
|
||||
import { Table, TableColumn } from '@/components/Table'
|
||||
import { ElButton, ElSwitch, ElRow, ElCol } from 'element-plus'
|
||||
import { ElSwitch, ElRow, ElCol } from 'element-plus'
|
||||
import { Search } from '@/components/Search'
|
||||
import { FormSchema } from '@/components/Form'
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
import { useDictStore } from '@/store/modules/dict'
|
||||
import { DictDetail } from '@/utils/dict'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { BaseButton } from '@/components/Button'
|
||||
|
||||
defineOptions({
|
||||
name: 'HelpIssue'
|
||||
@ -85,7 +86,7 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
const row = data.row
|
||||
return (
|
||||
<>
|
||||
<ElSwitch value={row.is_active} disabled />
|
||||
<ElSwitch modelValue={row.is_active} disabled />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -114,10 +115,10 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
const row = data.row
|
||||
return (
|
||||
<>
|
||||
<ElButton type="primary" link size="small" onClick={() => editAction(row)}>
|
||||
<BaseButton type="primary" link size="small" onClick={() => editAction(row)}>
|
||||
编辑
|
||||
</ElButton>
|
||||
<ElButton
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
type="danger"
|
||||
loading={delLoading.value}
|
||||
link
|
||||
@ -125,7 +126,7 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
onClick={() => delData(row)}
|
||||
>
|
||||
删除
|
||||
</ElButton>
|
||||
</BaseButton>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -224,7 +225,7 @@ const addAction = () => {
|
||||
<template #toolbar>
|
||||
<ElRow :gutter="10">
|
||||
<ElCol :span="1.5">
|
||||
<ElButton type="primary" @click="addAction">新增常见问题</ElButton>
|
||||
<BaseButton type="primary" @click="addAction">新增常见问题</BaseButton>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</template>
|
||||
|
@ -4,7 +4,7 @@ import { useForm } from '@/hooks/web/useForm'
|
||||
import { reactive, ref } from 'vue'
|
||||
import { useValidator } from '@/hooks/web/useValidator'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ElButton, ElMessage } from 'element-plus'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
// import { useTagsViewStore } from '@/store/modules/tagsView'
|
||||
import {
|
||||
@ -13,6 +13,7 @@ import {
|
||||
putIssueApi,
|
||||
getIssueCategoryOptionsApi
|
||||
} from '@/api/vadmin/help/issue'
|
||||
import { BaseButton } from '@/components/Button'
|
||||
|
||||
defineOptions({
|
||||
name: 'HelpIssueForm'
|
||||
@ -114,9 +115,9 @@ const formSchema = reactive<FormSchema[]>([
|
||||
default: () => {
|
||||
return (
|
||||
<>
|
||||
<ElButton loading={saveLoading.value} type="primary" onClick={submit}>
|
||||
<BaseButton loading={saveLoading.value} type="primary" onClick={submit}>
|
||||
立即保存
|
||||
</ElButton>
|
||||
</BaseButton>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
import { useTable } from '@/hooks/web/useTable'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { Table, TableColumn } from '@/components/Table'
|
||||
import { ElButton, ElSwitch, ElRow, ElCol } from 'element-plus'
|
||||
import { ElSwitch, ElRow, ElCol } from 'element-plus'
|
||||
import { Search } from '@/components/Search'
|
||||
import { FormSchema } from '@/components/Form'
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
@ -18,6 +18,7 @@ import Write from './components/Write.vue'
|
||||
import { Dialog } from '@/components/Dialog'
|
||||
import { useDictStore } from '@/store/modules/dict'
|
||||
import { selectDictLabel, DictDetail } from '@/utils/dict'
|
||||
import { BaseButton } from '@/components/Button'
|
||||
|
||||
defineOptions({
|
||||
name: 'HelpIssueCategory'
|
||||
@ -94,7 +95,7 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
const row = data.row
|
||||
return (
|
||||
<>
|
||||
<ElSwitch value={row.is_active} disabled />
|
||||
<ElSwitch modelValue={row.is_active} disabled />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -121,10 +122,10 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
const row = data.row
|
||||
return (
|
||||
<>
|
||||
<ElButton type="primary" link size="small" onClick={() => editAction(row)}>
|
||||
<BaseButton type="primary" link size="small" onClick={() => editAction(row)}>
|
||||
编辑
|
||||
</ElButton>
|
||||
<ElButton
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
type="danger"
|
||||
loading={delLoading.value}
|
||||
link
|
||||
@ -132,7 +133,7 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
onClick={() => delData(row)}
|
||||
>
|
||||
删除
|
||||
</ElButton>
|
||||
</BaseButton>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -276,7 +277,7 @@ const save = async () => {
|
||||
<template #toolbar>
|
||||
<ElRow :gutter="10">
|
||||
<ElCol :span="1.5">
|
||||
<ElButton type="primary" @click="addAction">新增常见问题类别</ElButton>
|
||||
<BaseButton type="primary" @click="addAction">新增常见问题类别</BaseButton>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</template>
|
||||
@ -287,10 +288,10 @@ const save = async () => {
|
||||
<Write ref="writeRef" :current-row="currentRow" />
|
||||
|
||||
<template #footer>
|
||||
<ElButton type="primary" :loading="saveLoading" @click="save">
|
||||
<BaseButton type="primary" :loading="saveLoading" @click="save">
|
||||
{{ t('exampleDemo.save') }}
|
||||
</ElButton>
|
||||
<ElButton @click="dialogVisible = false">{{ t('dialogDemo.close') }}</ElButton>
|
||||
</BaseButton>
|
||||
<BaseButton @click="dialogVisible = false">{{ t('dialogDemo.close') }}</BaseButton>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
@ -4,13 +4,14 @@ import { addImagesApi, getImagesListApi, delImagesListApi } from '@/api/vadmin/r
|
||||
import { useTable } from '@/hooks/web/useTable'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { Table, TableColumn } from '@/components/Table'
|
||||
import { ElButton, ElMessage, ElRow, ElCol, ElImage } from 'element-plus'
|
||||
import { ElMessage, ElRow, ElCol, ElImage } from 'element-plus'
|
||||
import { Search } from '@/components/Search'
|
||||
import { FormSchema } from '@/components/Form'
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
import Write from './components/Write.vue'
|
||||
import { Dialog } from '@/components/Dialog'
|
||||
import { useClipboard } from '@vueuse/core'
|
||||
import { BaseButton } from '@/components/Button'
|
||||
|
||||
defineOptions({
|
||||
name: 'ResourceImage'
|
||||
@ -123,13 +124,13 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
const row = data.row
|
||||
return (
|
||||
<>
|
||||
<ElButton type="primary" link size="small" onClick={() => toCopy(row.id)}>
|
||||
<BaseButton type="primary" link size="small" onClick={() => toCopy(row.id)}>
|
||||
复制编号
|
||||
</ElButton>
|
||||
<ElButton type="primary" link size="small" onClick={() => toCopy(row.image_url)}>
|
||||
</BaseButton>
|
||||
<BaseButton type="primary" link size="small" onClick={() => toCopy(row.image_url)}>
|
||||
复制链接
|
||||
</ElButton>
|
||||
<ElButton
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
type="danger"
|
||||
loading={delLoading.value}
|
||||
link
|
||||
@ -137,7 +138,7 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
onClick={() => delData(row)}
|
||||
>
|
||||
删除
|
||||
</ElButton>
|
||||
</BaseButton>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -259,8 +260,8 @@ const save = async () => {
|
||||
<template #toolbar>
|
||||
<ElRow :gutter="10">
|
||||
<ElCol :span="1.5">
|
||||
<ElButton type="primary" @click="addAction">新增图片素材</ElButton>
|
||||
<ElButton type="danger" @click="delData(null)">批量删除</ElButton>
|
||||
<BaseButton type="primary" @click="addAction">新增图片素材</BaseButton>
|
||||
<BaseButton type="danger" @click="delData(null)">批量删除</BaseButton>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</template>
|
||||
@ -271,10 +272,10 @@ const save = async () => {
|
||||
<Write ref="writeRef" :current-row="currentRow" />
|
||||
|
||||
<template #footer>
|
||||
<ElButton type="primary" :loading="saveLoading" @click="save">
|
||||
<BaseButton type="primary" :loading="saveLoading" @click="save">
|
||||
{{ t('exampleDemo.save') }}
|
||||
</ElButton>
|
||||
<ElButton @click="dialogVisible = false">{{ t('dialogDemo.close') }}</ElButton>
|
||||
</BaseButton>
|
||||
<BaseButton @click="dialogVisible = false">{{ t('dialogDemo.close') }}</BaseButton>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
@ -10,13 +10,14 @@ import {
|
||||
import { useTable } from '@/hooks/web/useTable'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { Table, TableColumn } from '@/components/Table'
|
||||
import { ElButton, ElSwitch, ElRow, ElCol } from 'element-plus'
|
||||
import { ElSwitch, ElRow, ElCol } from 'element-plus'
|
||||
import { Search } from '@/components/Search'
|
||||
import { FormSchema } from '@/components/Form'
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
import Write from './components/Write.vue'
|
||||
import { Dialog } from '@/components/Dialog'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { BaseButton } from '@/components/Button'
|
||||
|
||||
const props = defineProps({
|
||||
dictTypeId: propTypes.number.def(undefined)
|
||||
@ -79,7 +80,7 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
const row = data.row
|
||||
return (
|
||||
<>
|
||||
<ElSwitch value={!row.disabled} disabled />
|
||||
<ElSwitch modelValue={!row.disabled} disabled />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -105,10 +106,10 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
const row = data.row
|
||||
return (
|
||||
<>
|
||||
<ElButton type="primary" link size="small" onClick={() => editAction(row)}>
|
||||
<BaseButton type="primary" link size="small" onClick={() => editAction(row)}>
|
||||
编辑
|
||||
</ElButton>
|
||||
<ElButton
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
type="danger"
|
||||
loading={delLoading.value}
|
||||
link
|
||||
@ -116,7 +117,7 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
onClick={() => delData(row)}
|
||||
>
|
||||
删除
|
||||
</ElButton>
|
||||
</BaseButton>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -237,7 +238,7 @@ watch(
|
||||
<template #toolbar>
|
||||
<ElRow :gutter="10">
|
||||
<ElCol :span="1.5">
|
||||
<ElButton type="primary" @click="addAction">新增字典元素</ElButton>
|
||||
<BaseButton type="primary" @click="addAction">新增字典元素</BaseButton>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</template>
|
||||
@ -248,10 +249,10 @@ watch(
|
||||
<Write ref="writeRef" :current-row="currentRow" :dict-type-id="dictTypeId" />
|
||||
|
||||
<template #footer>
|
||||
<ElButton type="primary" :loading="saveLoading" @click="save">
|
||||
<BaseButton type="primary" :loading="saveLoading" @click="save">
|
||||
{{ t('exampleDemo.save') }}
|
||||
</ElButton>
|
||||
<ElButton @click="dialogVisible = false">{{ t('dialogDemo.close') }}</ElButton>
|
||||
</BaseButton>
|
||||
<BaseButton @click="dialogVisible = false">{{ t('dialogDemo.close') }}</BaseButton>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
import { useTable } from '@/hooks/web/useTable'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { Table, TableColumn } from '@/components/Table'
|
||||
import { ElButton, ElMessage, ElSwitch, ElRow, ElCol } from 'element-plus'
|
||||
import { ElMessage, ElSwitch, ElRow, ElCol } from 'element-plus'
|
||||
import { Search } from '@/components/Search'
|
||||
import { FormSchema } from '@/components/Form'
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
@ -18,6 +18,7 @@ import Write from './components/Write.vue'
|
||||
import { Dialog } from '@/components/Dialog'
|
||||
import { Icon } from '@/components/Icon'
|
||||
import { useClipboard } from '@vueuse/core'
|
||||
import { BaseButton } from '@/components/Button'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
@ -97,7 +98,7 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
const row = data.row
|
||||
return (
|
||||
<>
|
||||
<ElSwitch value={!row.disabled} disabled />
|
||||
<ElSwitch modelValue={!row.disabled} disabled />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -123,10 +124,10 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
const row = data.row
|
||||
return (
|
||||
<>
|
||||
<ElButton type="primary" link size="small" onClick={() => editAction(row)}>
|
||||
<BaseButton type="primary" link size="small" onClick={() => editAction(row)}>
|
||||
编辑
|
||||
</ElButton>
|
||||
<ElButton
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
type="danger"
|
||||
loading={delLoading.value}
|
||||
link
|
||||
@ -134,7 +135,7 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
onClick={() => delData(row)}
|
||||
>
|
||||
删除
|
||||
</ElButton>
|
||||
</BaseButton>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -273,10 +274,10 @@ const clearCurrentRow = async () => {
|
||||
<template #toolbar>
|
||||
<ElRow :gutter="10">
|
||||
<ElCol :span="1.5">
|
||||
<ElButton type="primary" @click="addAction">新增字典类型</ElButton>
|
||||
<BaseButton type="primary" @click="addAction">新增字典类型</BaseButton>
|
||||
</ElCol>
|
||||
<ElCol :span="1.5">
|
||||
<ElButton type="danger" @click="clearCurrentRow">清除选择</ElButton>
|
||||
<BaseButton type="danger" @click="clearCurrentRow">清除选择</BaseButton>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</template>
|
||||
@ -287,10 +288,10 @@ const clearCurrentRow = async () => {
|
||||
<Write ref="writeRef" :current-row="currentRow" />
|
||||
|
||||
<template #footer>
|
||||
<ElButton type="primary" :loading="saveLoading" @click="save">
|
||||
<BaseButton type="primary" :loading="saveLoading" @click="save">
|
||||
{{ t('exampleDemo.save') }}
|
||||
</ElButton>
|
||||
<ElButton @click="dialogVisible = false">{{ t('dialogDemo.close') }}</ElButton>
|
||||
</BaseButton>
|
||||
<BaseButton @click="dialogVisible = false">{{ t('dialogDemo.close') }}</BaseButton>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
@ -4,7 +4,7 @@ import { getRecordLoginListApi } from '@/api/vadmin/system/record/login'
|
||||
import { useTable } from '@/hooks/web/useTable'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { Table, TableColumn } from '@/components/Table'
|
||||
import { ElButton, ElSwitch } from 'element-plus'
|
||||
import { ElSwitch } from 'element-plus'
|
||||
import { Search } from '@/components/Search'
|
||||
import { FormSchema } from '@/components/Form'
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
@ -12,6 +12,7 @@ import Detail from './components/Detail.vue'
|
||||
import { Dialog } from '@/components/Dialog'
|
||||
import { selectDictLabel, DictDetail } from '@/utils/dict'
|
||||
import { useDictStore } from '@/store/modules/dict'
|
||||
import { BaseButton } from '@/components/Button'
|
||||
|
||||
defineOptions({
|
||||
name: 'SystemRecordLogin'
|
||||
@ -72,7 +73,7 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
default: (data: any) => {
|
||||
return (
|
||||
<>
|
||||
<ElSwitch value={data.row.status} size="small" disabled />
|
||||
<ElSwitch modelValue={data.row.status} size="small" disabled />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -168,9 +169,9 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
const row = data.row
|
||||
return (
|
||||
<>
|
||||
<ElButton type="primary" link onClick={() => action(row, 'detail')}>
|
||||
<BaseButton type="primary" link onClick={() => action(row, 'detail')}>
|
||||
详情
|
||||
</ElButton>
|
||||
</BaseButton>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -289,7 +290,7 @@ const action = (row: any, type: string) => {
|
||||
<Detail v-if="actionType === 'detail'" :current-row="currentRow" />
|
||||
|
||||
<template #footer>
|
||||
<ElButton @click="dialogVisible = false">{{ t('dialogDemo.close') }}</ElButton>
|
||||
<BaseButton @click="dialogVisible = false">{{ t('dialogDemo.close') }}</BaseButton>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
@ -46,7 +46,7 @@ const detailSchema = reactive<DescriptionsSchema[]>([
|
||||
default: (data: any) => {
|
||||
return (
|
||||
<>
|
||||
<ElSwitch value={data.status} size="small" disabled />
|
||||
<ElSwitch modelValue={data.status} size="small" disabled />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -4,12 +4,12 @@ import { getRecordOperationListApi } from '@/api/vadmin/system/record/operation'
|
||||
import { useTable } from '@/hooks/web/useTable'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { Table, TableColumn } from '@/components/Table'
|
||||
import { ElButton } from 'element-plus'
|
||||
import { Search } from '@/components/Search'
|
||||
import { FormSchema } from '@/components/Form'
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
import Detail from './components/Detail.vue'
|
||||
import { Dialog } from '@/components/Dialog'
|
||||
import { BaseButton } from '@/components/Button'
|
||||
|
||||
defineOptions({
|
||||
name: 'SystemRecordOperation'
|
||||
@ -141,9 +141,9 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
const row = data.row
|
||||
return (
|
||||
<>
|
||||
<ElButton type="primary" link onClick={() => action(row, 'detail')}>
|
||||
<BaseButton type="primary" link onClick={() => action(row, 'detail')}>
|
||||
详情
|
||||
</ElButton>
|
||||
</BaseButton>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -222,7 +222,7 @@ const action = (row: any, type: string) => {
|
||||
<Detail v-if="actionType === 'detail'" :current-row="currentRow" />
|
||||
|
||||
<template #footer>
|
||||
<ElButton @click="dialogVisible = false">{{ t('dialogDemo.close') }}</ElButton>
|
||||
<BaseButton @click="dialogVisible = false">{{ t('dialogDemo.close') }}</BaseButton>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
@ -3,7 +3,6 @@ import { reactive, ref, unref } from 'vue'
|
||||
import { useTable } from '@/hooks/web/useTable'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { Table, TableColumn } from '@/components/Table'
|
||||
import { ElButton } from 'element-plus'
|
||||
import { Search } from '@/components/Search'
|
||||
import { FormSchema } from '@/components/Form'
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
@ -13,6 +12,7 @@ import { selectDictLabel, DictDetail } from '@/utils/dict'
|
||||
import { useDictStore } from '@/store/modules/dict'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { getTaskRecordListApi } from '@/api/vadmin/system/task'
|
||||
import { BaseButton } from '@/components/Button'
|
||||
|
||||
defineOptions({
|
||||
name: 'SystemRecordTask'
|
||||
@ -144,9 +144,9 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
const row = data.row
|
||||
return (
|
||||
<>
|
||||
<ElButton type="primary" link size="small" onClick={() => view(row)}>
|
||||
<BaseButton type="primary" link size="small" onClick={() => view(row)}>
|
||||
详情
|
||||
</ElButton>
|
||||
</BaseButton>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -228,7 +228,7 @@ if (job_id) {
|
||||
<Detail v-if="actionType === 'detail'" :current-row="currentRow" />
|
||||
|
||||
<template #footer>
|
||||
<ElButton @click="dialogVisible = false">{{ t('dialogDemo.close') }}</ElButton>
|
||||
<BaseButton @click="dialogVisible = false">{{ t('dialogDemo.close') }}</BaseButton>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
@ -1,5 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
import { ElButton } from 'element-plus'
|
||||
import { getSystemSettingsApi, putSystemSettingsApi } from '@/api/vadmin/system/settings'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { Editor, EditorExpose } from '@/components/Editor'
|
||||
@ -74,7 +73,7 @@ getData()
|
||||
:editorConfig="editorConfig"
|
||||
/>
|
||||
<div class="mt-10px" style="float: right">
|
||||
<ElButton :loading="loading" type="primary" @click="save">立即保存</ElButton>
|
||||
<BaseButton :loading="loading" type="primary" @click="save">立即保存</BaseButton>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -1,26 +1,27 @@
|
||||
<script setup lang="tsx">
|
||||
import { Form, FormSchema } from '@/components/Form'
|
||||
import { useForm } from '@/hooks/web/useForm'
|
||||
import { ElButton, ElUpload } from 'element-plus'
|
||||
import { ElUpload } from 'element-plus'
|
||||
import { getSystemSettingsApi, putSystemSettingsApi } from '@/api/vadmin/system/settings'
|
||||
import { reactive, ref } from 'vue'
|
||||
import { reactive, ref, computed } from 'vue'
|
||||
import { ElMessage, ElIcon } from 'element-plus'
|
||||
import type { UploadProps } from 'element-plus'
|
||||
import { useStorage } from '@/hooks/web/useStorage'
|
||||
import { useAuthStore } from '@/store/modules/auth'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { Icon } from '@/components/Icon'
|
||||
import { useValidator } from '@/hooks/web/useValidator'
|
||||
import { BaseButton } from '@/components/Button'
|
||||
|
||||
const { required } = useValidator()
|
||||
const { getStorage } = useStorage()
|
||||
const authStore = useAuthStore()
|
||||
const appStore = useAppStore()
|
||||
|
||||
const props = defineProps({
|
||||
tabId: propTypes.number
|
||||
})
|
||||
|
||||
const token = getStorage(appStore.getToken)
|
||||
const token = computed(() => authStore.getToken)
|
||||
|
||||
const formSchema = reactive<FormSchema[]>([
|
||||
{
|
||||
@ -211,9 +212,9 @@ const formSchema = reactive<FormSchema[]>([
|
||||
default: () => {
|
||||
return (
|
||||
<>
|
||||
<ElButton loading={loading.value} type="primary" onClick={save}>
|
||||
<BaseButton loading={loading.value} type="primary" onClick={save}>
|
||||
立即提交
|
||||
</ElButton>
|
||||
</BaseButton>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
<script setup lang="tsx">
|
||||
import { Form, FormSchema } from '@/components/Form'
|
||||
import { useForm } from '@/hooks/web/useForm'
|
||||
import { ElButton } from 'element-plus'
|
||||
import { getSystemSettingsApi, putSystemSettingsApi } from '@/api/vadmin/system/settings'
|
||||
import { reactive, ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { useValidator } from '@/hooks/web/useValidator'
|
||||
import { BaseButton } from '@/components/Button'
|
||||
|
||||
const { required } = useValidator()
|
||||
|
||||
@ -78,9 +78,9 @@ const formSchema = reactive<FormSchema[]>([
|
||||
default: () => {
|
||||
return (
|
||||
<>
|
||||
<ElButton loading={loading.value} type="primary" onClick={save}>
|
||||
<BaseButton loading={loading.value} type="primary" onClick={save}>
|
||||
立即提交
|
||||
</ElButton>
|
||||
</BaseButton>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user