提交 55e9d9fc 作者: Vben

perf: optimize components and add comments

上级 84d9300e
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
"@iconify/iconify": "^2.0.1", "@iconify/iconify": "^2.0.1",
"@logicflow/core": "^0.4.11", "@logicflow/core": "^0.4.11",
"@logicflow/extension": "^0.4.12", "@logicflow/extension": "^0.4.12",
"@vueuse/core": "^4.11.2", "@vueuse/core": "^5.0.1",
"@zxcvbn-ts/core": "^0.3.0", "@zxcvbn-ts/core": "^0.3.0",
"ant-design-vue": "2.1.2", "ant-design-vue": "2.1.2",
"axios": "^0.21.1", "axios": "^0.21.1",
...@@ -63,7 +63,7 @@ ...@@ -63,7 +63,7 @@
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^12.1.4", "@commitlint/cli": "^12.1.4",
"@commitlint/config-conventional": "^12.1.4", "@commitlint/config-conventional": "^12.1.4",
"@iconify/json": "^1.1.353", "@iconify/json": "^1.1.354",
"@purge-icons/generated": "^0.7.0", "@purge-icons/generated": "^0.7.0",
"@types/codemirror": "^5.60.0", "@types/codemirror": "^5.60.0",
"@types/crypto-js": "^4.0.1", "@types/crypto-js": "^4.0.1",
...@@ -71,13 +71,13 @@ ...@@ -71,13 +71,13 @@
"@types/inquirer": "^7.3.1", "@types/inquirer": "^7.3.1",
"@types/lodash-es": "^4.17.4", "@types/lodash-es": "^4.17.4",
"@types/mockjs": "^1.0.3", "@types/mockjs": "^1.0.3",
"@types/node": "^15.12.1", "@types/node": "^15.12.2",
"@types/nprogress": "^0.2.0", "@types/nprogress": "^0.2.0",
"@types/qrcode": "^1.4.0", "@types/qrcode": "^1.4.0",
"@types/qs": "^6.9.6", "@types/qs": "^6.9.6",
"@types/sortablejs": "^1.10.6", "@types/sortablejs": "^1.10.6",
"@typescript-eslint/eslint-plugin": "^4.26.0", "@typescript-eslint/eslint-plugin": "^4.26.1",
"@typescript-eslint/parser": "^4.26.0", "@typescript-eslint/parser": "^4.26.1",
"@vitejs/plugin-legacy": "^1.4.1", "@vitejs/plugin-legacy": "^1.4.1",
"@vitejs/plugin-vue": "^1.2.3", "@vitejs/plugin-vue": "^1.2.3",
"@vitejs/plugin-vue-jsx": "^1.1.5", "@vitejs/plugin-vue-jsx": "^1.1.5",
...@@ -92,7 +92,7 @@ ...@@ -92,7 +92,7 @@
"eslint-define-config": "^1.0.8", "eslint-define-config": "^1.0.8",
"eslint-plugin-prettier": "^3.4.0", "eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-vue": "^7.10.0", "eslint-plugin-vue": "^7.10.0",
"esno": "^0.7.0", "esno": "^0.7.1",
"fs-extra": "^10.0.0", "fs-extra": "^10.0.0",
"http-server": "^0.12.3", "http-server": "^0.12.3",
"husky": "^6.0.0", "husky": "^6.0.0",
...@@ -121,14 +121,14 @@ ...@@ -121,14 +121,14 @@
"vite-plugin-style-import": "^0.10.1", "vite-plugin-style-import": "^0.10.1",
"vite-plugin-svg-icons": "^0.7.0", "vite-plugin-svg-icons": "^0.7.0",
"vite-plugin-theme": "^0.8.1", "vite-plugin-theme": "^0.8.1",
"vite-plugin-windicss": "^1.0.1", "vite-plugin-windicss": "^1.0.2",
"vue-eslint-parser": "^7.6.0", "vue-eslint-parser": "^7.6.0",
"vue-tsc": "^0.1.7" "vue-tsc": "^0.1.7"
}, },
"resolutions": { "resolutions": {
"//": "Used to install imagemin dependencies, because imagemin may not be installed in China. If it is abroad, you can delete it", "//": "Used to install imagemin dependencies, because imagemin may not be installed in China. If it is abroad, you can delete it",
"bin-wrapper": "npm:bin-wrapper-china", "bin-wrapper": "npm:bin-wrapper-china",
"rollup": "^2.51.0" "rollup": "^2.51.1"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
......
import AppLogo from './src/AppLogo.vue'; import { withInstall } from '/@/utils';
import AppProvider from './src/AppProvider.vue';
import AppSearch from './src/search/AppSearch.vue'; import appLogo from './src/AppLogo.vue';
import AppLocalePicker from './src/AppLocalePicker.vue'; import appProvider from './src/AppProvider.vue';
import AppDarkModeToggle from './src/AppDarkModeToggle.vue'; import appSearch from './src/search/AppSearch.vue';
import appLocalePicker from './src/AppLocalePicker.vue';
import appDarkModeToggle from './src/AppDarkModeToggle.vue';
export { useAppProviderContext } from './src/useAppContext'; export { useAppProviderContext } from './src/useAppContext';
export { AppLogo, AppProvider, AppSearch, AppLocalePicker, AppDarkModeToggle };
export const AppLogo = withInstall(appLogo);
export const AppProvider = withInstall(appProvider);
export const AppSearch = withInstall(appSearch);
export const AppLocalePicker = withInstall(appLocalePicker);
export const AppDarkModeToggle = withInstall(appDarkModeToggle);
<template> <template>
<div <div v-if="getShowDarkModeToggle" :class="getClass" @click="toggleDarkMode">
v-if="getShowDarkModeToggle"
:class="[
prefixCls,
{
[`${prefixCls}--dark`]: isDark,
},
]"
@click="toggleDarkMode"
>
<div :class="`${prefixCls}-inner`"> </div> <div :class="`${prefixCls}-inner`"> </div>
<SvgIcon size="14" name="sun" /> <SvgIcon size="14" name="sun" />
<SvgIcon size="14" name="moon" /> <SvgIcon size="14" name="moon" />
...@@ -16,25 +7,29 @@ ...@@ -16,25 +7,29 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, computed } from 'vue'; import { defineComponent, computed } from 'vue';
import { useDesign } from '/@/hooks/web/useDesign';
import { SvgIcon } from '/@/components/Icon'; import { SvgIcon } from '/@/components/Icon';
import { useDesign } from '/@/hooks/web/useDesign';
import { useRootSetting } from '/@/hooks/setting/useRootSetting'; import { useRootSetting } from '/@/hooks/setting/useRootSetting';
import { updateHeaderBgColor, updateSidebarBgColor } from '/@/logics/theme/updateBackground'; import { updateHeaderBgColor, updateSidebarBgColor } from '/@/logics/theme/updateBackground';
import { updateDarkTheme } from '/@/logics/theme/dark'; import { updateDarkTheme } from '/@/logics/theme/dark';
import { ThemeEnum } from '/@/enums/appEnum'; import { ThemeEnum } from '/@/enums/appEnum';
export default defineComponent({ export default defineComponent({
name: 'DarkModeToggle', name: 'DarkModeToggle',
components: { SvgIcon }, components: { SvgIcon },
setup() { setup() {
const { prefixCls } = useDesign('dark-mode-toggle'); const { prefixCls } = useDesign('dark-switch');
const { getDarkMode, setDarkMode, getShowDarkModeToggle } = useRootSetting(); const { getDarkMode, setDarkMode, getShowDarkModeToggle } = useRootSetting();
const isDark = computed(() => getDarkMode.value === ThemeEnum.DARK); const isDark = computed(() => getDarkMode.value === ThemeEnum.DARK);
const getClass = computed(() => [
prefixCls,
{
[`${prefixCls}--dark`]: isDark,
},
]);
function toggleDarkMode() { function toggleDarkMode() {
const darkMode = getDarkMode.value === ThemeEnum.DARK ? ThemeEnum.LIGHT : ThemeEnum.DARK; const darkMode = getDarkMode.value === ThemeEnum.DARK ? ThemeEnum.LIGHT : ThemeEnum.DARK;
setDarkMode(darkMode); setDarkMode(darkMode);
...@@ -44,6 +39,7 @@ ...@@ -44,6 +39,7 @@
} }
return { return {
getClass,
isDark, isDark,
prefixCls, prefixCls,
toggleDarkMode, toggleDarkMode,
...@@ -53,7 +49,7 @@ ...@@ -53,7 +49,7 @@
}); });
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
@prefix-cls: ~'@{namespace}-dark-mode-toggle'; @prefix-cls: ~'@{namespace}-dark-switch';
html[data-theme='dark'] { html[data-theme='dark'] {
.@{prefix-cls} { .@{prefix-cls} {
......
...@@ -13,39 +13,44 @@ ...@@ -13,39 +13,44 @@
> >
<span class="cursor-pointer flex items-center"> <span class="cursor-pointer flex items-center">
<Icon icon="ion:language" /> <Icon icon="ion:language" />
<span v-if="showText" class="ml-1">{{ getLangText }}</span> <span v-if="showText" class="ml-1">{{ getLocaleText }}</span>
</span> </span>
</Dropdown> </Dropdown>
</template> </template>
<script lang="ts"> <script lang="ts">
import type { LocaleType } from '/#/config'; import type { LocaleType } from '/#/config';
import type { DropMenu } from '/@/components/Dropdown'; import type { DropMenu } from '/@/components/Dropdown';
import { defineComponent, ref, watchEffect, unref, computed } from 'vue'; import { defineComponent, ref, watchEffect, unref, computed } from 'vue';
import { Dropdown } from '/@/components/Dropdown'; import { Dropdown } from '/@/components/Dropdown';
import Icon from '/@/components/Icon'; import { Icon } from '/@/components/Icon';
import { useLocale } from '/@/locales/useLocale'; import { useLocale } from '/@/locales/useLocale';
import { localeList } from '/@/settings/localeSetting'; import { localeList } from '/@/settings/localeSetting';
import { propTypes } from '/@/utils/propTypes';
const props = {
/**
* Whether to display text
*/
showText: { type: Boolean, default: true },
/**
* Whether to refresh the interface when changing
*/
reload: { type: Boolean },
};
export default defineComponent({ export default defineComponent({
name: 'AppLocalPicker', name: 'AppLocalPicker',
components: { Dropdown, Icon }, components: { Dropdown, Icon },
props: { props,
// Whether to display text
showText: propTypes.bool.def(true),
// Whether to refresh the interface when changing
reload: propTypes.bool,
},
setup(props) { setup(props) {
const selectedKeys = ref<string[]>([]); const selectedKeys = ref<string[]>([]);
const { changeLocale, getLocale } = useLocale(); const { changeLocale, getLocale } = useLocale();
const getLangText = computed(() => { const getLocaleText = computed(() => {
const key = selectedKeys.value[0]; const key = selectedKeys.value[0];
if (!key) return ''; if (!key) {
return '';
}
return localeList.find((item) => item.event === key)?.text; return localeList.find((item) => item.event === key)?.text;
}); });
...@@ -60,11 +65,13 @@ ...@@ -60,11 +65,13 @@
} }
function handleMenuEvent(menu: DropMenu) { function handleMenuEvent(menu: DropMenu) {
if (unref(getLocale) === menu.event) return; if (unref(getLocale) === menu.event) {
return;
}
toggleLocale(menu.event as string); toggleLocale(menu.event as string);
} }
return { localeList, handleMenuEvent, selectedKeys, getLangText }; return { localeList, handleMenuEvent, selectedKeys, getLocaleText };
}, },
}); });
</script> </script>
......
...@@ -3,63 +3,69 @@ ...@@ -3,63 +3,69 @@
* @Description: logo component * @Description: logo component
--> -->
<template> <template>
<div <div class="anticon" :class="getAppLogoClass" @click="goHome">
class="anticon"
:class="[prefixCls, theme, { 'collapsed-show-title': getCollapsedShowTitle }]"
@click="handleGoHome"
>
<img src="../../../assets/images/logo.png" /> <img src="../../../assets/images/logo.png" />
<div <div class="ml-2 truncate md:opacity-100" :class="getTitleClass" v-show="showTitle">
class="ml-2 truncate md:opacity-100"
:class="[
`${prefixCls}__title`,
{
'xs:opacity-0': !alwaysShowTitle,
},
]"
v-show="showTitle"
>
{{ title }} {{ title }}
</div> </div>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent, computed, unref } from 'vue';
import { useGlobSetting } from '/@/hooks/setting'; import { useGlobSetting } from '/@/hooks/setting';
import { useGo } from '/@/hooks/web/usePage'; import { useGo } from '/@/hooks/web/usePage';
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
import { PageEnum } from '/@/enums/pageEnum'; import { PageEnum } from '/@/enums/pageEnum';
import { propTypes } from '/@/utils/propTypes';
const props = {
/**
* The theme of the current parent component
*/
theme: { type: String, validator: (v) => ['light', 'dark'].includes(v) },
/**
* Whether to show title
*/
showTitle: { type: Boolean, default: true },
/**
* The title is also displayed when the menu is collapsed
*/
alwaysShowTitle: { type: Boolean },
};
export default defineComponent({ export default defineComponent({
name: 'AppLogo', name: 'AppLogo',
props: { props: props,
/** setup(props) {
* The theme of the current parent component
*/
theme: propTypes.oneOf(['light', 'dark']),
// Whether to show title
showTitle: propTypes.bool.def(true),
alwaysShowTitle: propTypes.bool.def(false),
},
setup() {
const { prefixCls } = useDesign('app-logo'); const { prefixCls } = useDesign('app-logo');
const { getCollapsedShowTitle } = useMenuSetting(); const { getCollapsedShowTitle } = useMenuSetting();
const { title } = useGlobSetting(); const { title } = useGlobSetting();
const go = useGo(); const go = useGo();
function handleGoHome(): void { const getAppLogoClass = computed(() => [
prefixCls,
props.theme,
{ 'collapsed-show-title': unref(getCollapsedShowTitle) },
]);
const getTitleClass = computed(() => [
`${prefixCls}__title`,
{
'xs:opacity-0': !props.alwaysShowTitle,
},
]);
function goHome() {
go(PageEnum.BASE_HOME); go(PageEnum.BASE_HOME);
} }
return { return {
handleGoHome, getAppLogoClass,
getTitleClass,
getCollapsedShowTitle,
goHome,
title, title,
prefixCls, prefixCls,
getCollapsedShowTitle,
}; };
}, },
}); });
......
<script lang="ts"> <script lang="ts">
import { defineComponent, toRefs, ref, unref } from 'vue'; import { defineComponent, toRefs, ref, unref } from 'vue';
import { createAppProviderContext } from './useAppContext'; import { createAppProviderContext } from './useAppContext';
import { prefixCls } from '/@/settings/designSetting';
import { createBreakpointListen } from '/@/hooks/event/useBreakpoint'; import { createBreakpointListen } from '/@/hooks/event/useBreakpoint';
import { propTypes } from '/@/utils/propTypes'; import { prefixCls } from '/@/settings/designSetting';
import { useAppStore } from '/@/store/modules/app'; import { useAppStore } from '/@/store/modules/app';
import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum'; import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
const props = {
/**
* class style prefix
*/
prefixCls: { type: String, default: prefixCls },
};
export default defineComponent({ export default defineComponent({
name: 'AppProvider', name: 'AppProvider',
inheritAttrs: false, inheritAttrs: false,
props: { props,
prefixCls: propTypes.string.def(prefixCls),
},
setup(props, { slots }) { setup(props, { slots }) {
const isMobile = ref(false); const isMobile = ref(false);
const isSetState = ref(false); const isSetState = ref(false);
const appStore = useAppStore(); const appStore = useAppStore();
// Monitor screen breakpoint information changes
createBreakpointListen(({ screenMap, sizeEnum, width }) => { createBreakpointListen(({ screenMap, sizeEnum, width }) => {
const lgWidth = screenMap.get(sizeEnum.LG); const lgWidth = screenMap.get(sizeEnum.LG);
if (lgWidth) { if (lgWidth) {
...@@ -30,8 +33,13 @@ ...@@ -30,8 +33,13 @@
}); });
const { prefixCls } = toRefs(props); const { prefixCls } = toRefs(props);
// Inject variables into the global
createAppProviderContext({ prefixCls, isMobile }); createAppProviderContext({ prefixCls, isMobile });
/**
* Used to maintain the state before the window changes
*/
function handleRestoreState() { function handleRestoreState() {
if (unref(isMobile)) { if (unref(isMobile)) {
if (!unref(isSetState)) { if (!unref(isSetState)) {
......
...@@ -4,11 +4,11 @@ ...@@ -4,11 +4,11 @@
import { SearchOutlined } from '@ant-design/icons-vue'; import { SearchOutlined } from '@ant-design/icons-vue';
import AppSearchModal from './AppSearchModal.vue'; import AppSearchModal from './AppSearchModal.vue';
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
export default defineComponent({ export default defineComponent({
name: 'AppSearch', name: 'AppSearch',
setup() { setup() {
const showModal = ref(false); const showModal = ref(false);
const { t } = useI18n(); const { t } = useI18n();
function changeModal(show: boolean) { function changeModal(show: boolean) {
......
<template> <template>
<div :class="`${prefixCls}`"> <div :class="`${prefixCls}`">
<AppSearchKeyItem :class="`${prefixCls}__item`" icon="ant-design:enter-outlined" /> <AppSearchKeyItem :class="`${prefixCls}-item`" icon="ant-design:enter-outlined" />
<span>{{ t('component.app.toSearch') }}</span> <span>{{ t('component.app.toSearch') }}</span>
<AppSearchKeyItem :class="`${prefixCls}__item`" icon="ion:arrow-up-outline" /> <AppSearchKeyItem :class="`${prefixCls}-item`" icon="ion:arrow-up-outline" />
<AppSearchKeyItem :class="`${prefixCls}__item`" icon="ion:arrow-down-outline" /> <AppSearchKeyItem :class="`${prefixCls}-item`" icon="ion:arrow-down-outline" />
<span>{{ t('component.app.toNavigate') }}</span> <span>{{ t('component.app.toNavigate') }}</span>
<AppSearchKeyItem :class="`${prefixCls}__item`" icon="mdi:keyboard-esc" /> <AppSearchKeyItem :class="`${prefixCls}-item`" icon="mdi:keyboard-esc" />
<span>{{ t('common.closeText') }}</span> <span>{{ t('common.closeText') }}</span>
</div> </div>
</template> </template>
...@@ -21,10 +21,7 @@ ...@@ -21,10 +21,7 @@
setup() { setup() {
const { prefixCls } = useDesign('app-search-footer'); const { prefixCls } = useDesign('app-search-footer');
const { t } = useI18n(); const { t } = useI18n();
return { return { prefixCls, t };
prefixCls,
t,
};
}, },
}); });
</script> </script>
...@@ -44,7 +41,7 @@ ...@@ -44,7 +41,7 @@
align-items: center; align-items: center;
flex-shrink: 0; flex-shrink: 0;
&__item { &-item {
display: flex; display: flex;
width: 20px; width: 20px;
height: 18px; height: 18px;
......
...@@ -8,8 +8,6 @@ ...@@ -8,8 +8,6 @@
import { Icon } from '/@/components/Icon'; import { Icon } from '/@/components/Icon';
export default defineComponent({ export default defineComponent({
components: { Icon }, components: { Icon },
props: { props: { icon: String },
icon: String,
},
}); });
</script> </script>
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
@change="handleSearch" @change="handleSearch"
> >
<template #prefix> <template #prefix>
<!-- <Icon icon="ion:search"/> -->
<SearchOutlined /> <SearchOutlined />
</template> </template>
</Input> </Input>
...@@ -59,21 +58,20 @@ ...@@ -59,21 +58,20 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, computed, unref, ref, watch, nextTick } from 'vue'; import { defineComponent, computed, unref, ref, watch, nextTick } from 'vue';
import { SearchOutlined } from '@ant-design/icons-vue'; import { SearchOutlined } from '@ant-design/icons-vue';
import { Input } from 'ant-design-vue'; import { Input } from 'ant-design-vue';
import AppSearchFooter from './AppSearchFooter.vue'; import AppSearchFooter from './AppSearchFooter.vue';
import Icon from '/@/components/Icon'; import Icon from '/@/components/Icon';
import clickOutside from '/@/directives/clickOutside'; import clickOutside from '/@/directives/clickOutside';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
import { useRefs } from '/@/hooks/core/useRefs'; import { useRefs } from '/@/hooks/core/useRefs';
import { useMenuSearch } from './useMenuSearch'; import { useMenuSearch } from './useMenuSearch';
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
import { useAppInject } from '/@/hooks/web/useAppInject'; import { useAppInject } from '/@/hooks/web/useAppInject';
import { propTypes } from '/@/utils/propTypes'; const props = {
visible: { type: Boolean },
};
export default defineComponent({ export default defineComponent({
name: 'AppSearchModal', name: 'AppSearchModal',
...@@ -81,17 +79,16 @@ ...@@ -81,17 +79,16 @@
directives: { directives: {
clickOutside, clickOutside,
}, },
props: { props,
visible: propTypes.bool,
},
emits: ['close'], emits: ['close'],
setup(props, { emit }) { setup(props, { emit }) {
const scrollWrap = ref<ElRef>(null); const scrollWrap = ref<ElRef>(null);
const { prefixCls } = useDesign('app-search-modal'); const inputRef = ref<Nullable<HTMLElement>>(null);
const { t } = useI18n(); const { t } = useI18n();
const { prefixCls } = useDesign('app-search-modal');
const [refs, setRefs] = useRefs(); const [refs, setRefs] = useRefs();
const { getIsMobile } = useAppInject(); const { getIsMobile } = useAppInject();
const inputRef = ref<Nullable<HTMLElement>>(null);
const { handleSearch, searchResult, keyword, activeIndex, handleEnter, handleMouseenter } = const { handleSearch, searchResult, keyword, activeIndex, handleEnter, handleMouseenter } =
useMenuSearch(refs, scrollWrap, emit); useMenuSearch(refs, scrollWrap, emit);
...@@ -109,8 +106,8 @@ ...@@ -109,8 +106,8 @@
watch( watch(
() => props.visible, () => props.visible,
(v: boolean) => { (visible: boolean) => {
v && visible &&
nextTick(() => { nextTick(() => {
unref(inputRef)?.focus(); unref(inputRef)?.focus();
}); });
......
import type { Menu } from '/@/router/types'; import type { Menu } from '/@/router/types';
import { ref, onBeforeMount, unref, Ref, nextTick } from 'vue'; import { ref, onBeforeMount, unref, Ref, nextTick } from 'vue';
import { getMenus } from '/@/router/menus'; import { getMenus } from '/@/router/menus';
import { cloneDeep } from 'lodash-es'; import { cloneDeep } from 'lodash-es';
import { filter, forEach } from '/@/utils/helper/treeHelper'; import { filter, forEach } from '/@/utils/helper/treeHelper';
import { useGo } from '/@/hooks/web/usePage'; import { useGo } from '/@/hooks/web/usePage';
import { useScrollTo } from '/@/hooks/event/useScrollTo'; import { useScrollTo } from '/@/hooks/event/useScrollTo';
import { onKeyStroke, useDebounceFn } from '@vueuse/core'; import { onKeyStroke, useDebounceFn } from '@vueuse/core';
...@@ -67,7 +63,6 @@ export function useMenuSearch(refs: Ref<HTMLElement[]>, scrollWrap: Ref<ElRef>, ...@@ -67,7 +63,6 @@ export function useMenuSearch(refs: Ref<HTMLElement[]>, scrollWrap: Ref<ElRef>,
function handlerSearchResult(filterMenu: Menu[], reg: RegExp, parent?: Menu) { function handlerSearchResult(filterMenu: Menu[], reg: RegExp, parent?: Menu) {
const ret: SearchResult[] = []; const ret: SearchResult[] = [];
filterMenu.forEach((item) => { filterMenu.forEach((item) => {
const { name, path, icon, children } = item; const { name, path, icon, children } = item;
if (reg.test(name) && !children?.length) { if (reg.test(name) && !children?.length) {
...@@ -84,11 +79,13 @@ export function useMenuSearch(refs: Ref<HTMLElement[]>, scrollWrap: Ref<ElRef>, ...@@ -84,11 +79,13 @@ export function useMenuSearch(refs: Ref<HTMLElement[]>, scrollWrap: Ref<ElRef>,
return ret; return ret;
} }
// Activate when the mouse moves to a certain line
function handleMouseenter(e: any) { function handleMouseenter(e: any) {
const index = e.target.dataset.index; const index = e.target.dataset.index;
activeIndex.value = Number(index); activeIndex.value = Number(index);
} }
// Arrow key up
function handleUp() { function handleUp() {
if (!searchResult.value.length) return; if (!searchResult.value.length) return;
activeIndex.value--; activeIndex.value--;
...@@ -98,6 +95,7 @@ export function useMenuSearch(refs: Ref<HTMLElement[]>, scrollWrap: Ref<ElRef>, ...@@ -98,6 +95,7 @@ export function useMenuSearch(refs: Ref<HTMLElement[]>, scrollWrap: Ref<ElRef>,
handleScroll(); handleScroll();
} }
// Arrow key down
function handleDown() { function handleDown() {
if (!searchResult.value.length) return; if (!searchResult.value.length) return;
activeIndex.value++; activeIndex.value++;
...@@ -107,15 +105,23 @@ export function useMenuSearch(refs: Ref<HTMLElement[]>, scrollWrap: Ref<ElRef>, ...@@ -107,15 +105,23 @@ export function useMenuSearch(refs: Ref<HTMLElement[]>, scrollWrap: Ref<ElRef>,
handleScroll(); handleScroll();
} }
// When the keyboard up and down keys move to an invisible place
// the scroll bar needs to scroll automatically
function handleScroll() { function handleScroll() {
const refList = unref(refs); const refList = unref(refs);
if (!refList || !Array.isArray(refList) || refList.length === 0 || !unref(scrollWrap)) return; if (!refList || !Array.isArray(refList) || refList.length === 0 || !unref(scrollWrap)) {
return;
}
const index = unref(activeIndex); const index = unref(activeIndex);
const currentRef = refList[index]; const currentRef = refList[index];
if (!currentRef) return; if (!currentRef) {
return;
}
const wrapEl = unref(scrollWrap); const wrapEl = unref(scrollWrap);
if (!wrapEl) return; if (!wrapEl) {
return;
}
const scrollHeight = currentRef.offsetTop + currentRef.offsetHeight; const scrollHeight = currentRef.offsetTop + currentRef.offsetHeight;
const wrapHeight = wrapEl.offsetHeight; const wrapHeight = wrapEl.offsetHeight;
const { start } = useScrollTo({ const { start } = useScrollTo({
...@@ -126,8 +132,11 @@ export function useMenuSearch(refs: Ref<HTMLElement[]>, scrollWrap: Ref<ElRef>, ...@@ -126,8 +132,11 @@ export function useMenuSearch(refs: Ref<HTMLElement[]>, scrollWrap: Ref<ElRef>,
start(); start();
} }
// enter keyboard event
async function handleEnter() { async function handleEnter() {
if (!searchResult.value.length) return; if (!searchResult.value.length) {
return;
}
const result = unref(searchResult); const result = unref(searchResult);
const index = unref(activeIndex); const index = unref(activeIndex);
if (result.length === 0 || index < 0) { if (result.length === 0 || index < 0) {
...@@ -139,14 +148,18 @@ export function useMenuSearch(refs: Ref<HTMLElement[]>, scrollWrap: Ref<ElRef>, ...@@ -139,14 +148,18 @@ export function useMenuSearch(refs: Ref<HTMLElement[]>, scrollWrap: Ref<ElRef>,
go(to.path); go(to.path);
} }
// close search modal
function handleClose() { function handleClose() {
searchResult.value = []; searchResult.value = [];
emit('close'); emit('close');
} }
// enter search
onKeyStroke('Enter', handleEnter); onKeyStroke('Enter', handleEnter);
// Monitor keyboard arrow keys
onKeyStroke('ArrowUp', handleUp); onKeyStroke('ArrowUp', handleUp);
onKeyStroke('ArrowDown', handleDown); onKeyStroke('ArrowDown', handleDown);
// esc close
onKeyStroke('Escape', handleClose); onKeyStroke('Escape', handleClose);
return { handleSearch, searchResult, keyword, activeIndex, handleMouseenter, handleEnter }; return { handleSearch, searchResult, keyword, activeIndex, handleMouseenter, handleEnter };
......
...@@ -3,7 +3,6 @@ import { createContext, useContext } from '/@/hooks/core/useContext'; ...@@ -3,7 +3,6 @@ import { createContext, useContext } from '/@/hooks/core/useContext';
export interface AppProviderContextProps { export interface AppProviderContextProps {
prefixCls: Ref<string>; prefixCls: Ref<string>;
isMobile: Ref<boolean>; isMobile: Ref<boolean>;
} }
......
import Authority from './src/Authority.vue'; import { withInstall } from '/@/utils';
import authority from './src/Authority.vue';
export { Authority }; export const Authority = withInstall(authority);
...@@ -4,11 +4,8 @@ ...@@ -4,11 +4,8 @@
<script lang="ts"> <script lang="ts">
import type { PropType } from 'vue'; import type { PropType } from 'vue';
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { RoleEnum } from '/@/enums/roleEnum'; import { RoleEnum } from '/@/enums/roleEnum';
import { usePermission } from '/@/hooks/web/usePermission'; import { usePermission } from '/@/hooks/web/usePermission';
import { getSlot } from '/@/utils/helper/tsxHelper'; import { getSlot } from '/@/utils/helper/tsxHelper';
export default defineComponent({ export default defineComponent({
......
import BasicArrow from './src/BasicArrow.vue'; import { withInstall } from '/@/utils';
import BasicTitle from './src/BasicTitle.vue'; import basicArrow from './src/BasicArrow.vue';
import BasicHelp from './src/BasicHelp.vue'; import basicTitle from './src/BasicTitle.vue';
import basicHelp from './src/BasicHelp.vue';
export { BasicArrow, BasicTitle, BasicHelp }; export const BasicArrow = withInstall(basicArrow);
export const BasicTitle = withInstall(basicTitle);
export const BasicHelp = withInstall(basicHelp);
...@@ -9,41 +9,50 @@ ...@@ -9,41 +9,50 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, computed } from 'vue'; import { defineComponent, computed } from 'vue';
import { Icon } from '/@/components/Icon';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
import { propTypes } from '/@/utils/propTypes'; const props = {
/**
import { Icon } from '/@/components/Icon'; * Arrow expand state
*/
expand: { type: Boolean },
/**
* Arrow up by default
*/
up: { type: Boolean },
/**
* Arrow down by default
*/
down: { type: Boolean },
/**
* Cancel padding/margin for inline
*/
inset: { type: Boolean },
};
export default defineComponent({ export default defineComponent({
name: 'BasicArrow', name: 'BasicArrow',
components: { Icon }, components: { Icon },
props: { props,
expand: propTypes.bool,
top: propTypes.bool,
bottom: propTypes.bool,
inset: propTypes.bool,
},
setup(props) { setup(props) {
const { prefixCls } = useDesign('basic-arrow'); const { prefixCls } = useDesign('basic-arrow');
// get component class
const getClass = computed(() => { const getClass = computed(() => {
const { expand, top, bottom, inset } = props; const { expand, up, down, inset } = props;
return [ return [
prefixCls, prefixCls,
{ {
[`${prefixCls}--active`]: expand, [`${prefixCls}--active`]: expand,
top, up,
inset, inset,
bottom, down,
}, },
]; ];
}); });
return { return { getClass };
getClass,
};
}, },
}); });
</script> </script>
...@@ -65,19 +74,19 @@ ...@@ -65,19 +74,19 @@
line-height: 0px; line-height: 0px;
} }
&.top { &.up {
transform: rotate(-90deg); transform: rotate(-90deg);
} }
&.bottom { &.down {
transform: rotate(90deg); transform: rotate(90deg);
} }
&.top.@{prefix-cls}--active { &.up.@{prefix-cls}--active {
transform: rotate(90deg); transform: rotate(90deg);
} }
&.bottom.@{prefix-cls}--active { &.down.@{prefix-cls}--active {
transform: rotate(-90deg); transform: rotate(-90deg);
} }
} }
......
<script lang="tsx"> <script lang="tsx">
import type { CSSProperties, PropType } from 'vue'; import type { CSSProperties, PropType } from 'vue';
import { defineComponent, computed, unref } from 'vue'; import { defineComponent, computed, unref } from 'vue';
import { Tooltip } from 'ant-design-vue'; import { Tooltip } from 'ant-design-vue';
import { InfoCircleOutlined } from '@ant-design/icons-vue'; import { InfoCircleOutlined } from '@ant-design/icons-vue';
import { getPopupContainer } from '/@/utils'; import { getPopupContainer } from '/@/utils';
import { isString, isArray } from '/@/utils/is'; import { isString, isArray } from '/@/utils/is';
import { getSlot } from '/@/utils/helper/tsxHelper'; import { getSlot } from '/@/utils/helper/tsxHelper';
import { propTypes } from '/@/utils/propTypes';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
const props = {
/**
* Help text max-width
* @default: 600px
*/
maxWidth: { type: String, default: '600px' },
/**
* Whether to display the serial number
* @default: false
*/
showIndex: { type: Boolean },
/**
* Help text font color
* @default: #ffffff
*/
color: { type: String, default: '#ffffff' },
/**
* Help text font size
* @default: 14px
*/
fontSize: { type: String, default: '14px' },
/**
* Help text list
*/
placement: { type: String, default: 'right' },
/**
* Help text list
*/
text: { type: [Array, String] as PropType<string[] | string> },
};
export default defineComponent({ export default defineComponent({
name: 'BasicHelp', name: 'BasicHelp',
components: { Tooltip }, components: { Tooltip },
props: { props,
// max-width
maxWidth: propTypes.string.def('600px'),
// Whether to display the serial number
showIndex: propTypes.bool,
// color
color: propTypes.string.def('#ffffff'),
fontSize: propTypes.string.def('14px'),
placement: propTypes.string.def('right'),
absolute: propTypes.bool,
// Text list
text: {
type: [Array, String] as PropType<string[] | string>,
},
// 定位
position: {
type: [Object] as PropType<any>,
default: () => ({
position: 'absolute',
left: 0,
bottom: 0,
}),
},
},
setup(props, { slots }) { setup(props, { slots }) {
const { prefixCls } = useDesign('basic-help'); const { prefixCls } = useDesign('basic-help');
const getOverlayStyle = computed( const getTooltipStyle = computed(
(): CSSProperties => { (): CSSProperties => ({ color: props.color, fontSize: props.fontSize })
return {
maxWidth: props.maxWidth,
};
}
);
const getWrapStyle = computed(
(): CSSProperties => {
return {
color: props.color,
fontSize: props.fontSize,
};
}
); );
const getMainStyleRef = computed(() => { const getOverlayStyle = computed((): CSSProperties => ({ maxWidth: props.maxWidth }));
return props.absolute ? props.position : {};
});
const renderTitle = () => { function renderTitle() {
const list = props.text; const textList = props.text;
if (isString(list)) { if (isString(textList)) {
return <p>{list}</p>; return <p>{textList}</p>;
} }
if (isArray(list)) { if (isArray(textList)) {
return list.map((item, index) => { return textList.map((text, index) => {
return ( return (
<p key={item}> <p key={text}>
<> <>
{props.showIndex ? `${index + 1}. ` : ''} {props.showIndex ? `${index + 1}. ` : ''}
{item} {text}
</> </>
</p> </p>
); );
}); });
} }
return null; return null;
}; }
return () => { return () => {
return ( return (
<Tooltip <Tooltip
title={<div style={unref(getWrapStyle)}>{renderTitle()}</div>}
overlayClassName={`${prefixCls}__wrap`} overlayClassName={`${prefixCls}__wrap`}
title={<div style={unref(getTooltipStyle)}>{renderTitle()}</div>}
autoAdjustOverflow={true} autoAdjustOverflow={true}
overlayStyle={unref(getOverlayStyle)} overlayStyle={unref(getOverlayStyle)}
placement={props.placement as 'left'} placement={props.placement as 'right'}
getPopupContainer={() => getPopupContainer()} getPopupContainer={() => getPopupContainer()}
> >
<span class={prefixCls} style={unref(getMainStyleRef)}> <span class={prefixCls}>{getSlot(slots) || <InfoCircleOutlined />}</span>
{getSlot(slots) || <InfoCircleOutlined />}
</span>
</Tooltip> </Tooltip>
); );
}; };
......
<template> <template>
<span :class="getClass"> <span :class="getClass">
<slot></slot> <slot></slot>
<BasicHelp :class="`${prefixCls}__help`" v-if="helpMessage" :text="helpMessage" /> <BasicHelp :class="`${prefixCls}-help`" v-if="helpMessage" :text="helpMessage" />
</span> </span>
</template> </template>
<script lang="ts"> <script lang="ts">
import type { PropType } from 'vue'; import type { PropType } from 'vue';
import { defineComponent, computed } from 'vue'; import { defineComponent, computed } from 'vue';
import BasicHelp from './BasicHelp.vue'; import BasicHelp from './BasicHelp.vue';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
import { propTypes } from '/@/utils/propTypes'; const props = {
/**
* Help text list or string
* @default: ''
*/
helpMessage: {
type: [String, Array] as PropType<string | string[]>,
default: '',
},
/**
* Whether the color block on the left side of the title
* @default: false
*/
span: { type: Boolean },
/**
* Whether to default the text, that is, not bold
* @default: false
*/
normal: { type: Boolean },
};
export default defineComponent({ export default defineComponent({
name: 'BasicTitle', name: 'BasicTitle',
components: { BasicHelp }, components: { BasicHelp },
props: { props,
helpMessage: {
type: [String, Array] as PropType<string | string[]>,
default: '',
},
span: propTypes.bool,
normal: propTypes.bool.def(false),
},
setup(props, { slots }) { setup(props, { slots }) {
const { prefixCls } = useDesign('basic-title'); const { prefixCls } = useDesign('basic-title');
...@@ -33,6 +43,7 @@ ...@@ -33,6 +43,7 @@
{ [`${prefixCls}-show-span`]: props.span && slots.default }, { [`${prefixCls}-show-span`]: props.span && slots.default },
{ [`${prefixCls}-normal`]: props.normal }, { [`${prefixCls}-normal`]: props.normal },
]); ]);
return { prefixCls, getClass }; return { prefixCls, getClass };
}, },
}); });
...@@ -67,7 +78,7 @@ ...@@ -67,7 +78,7 @@
content: ''; content: '';
} }
&__help { &-help {
margin-left: 10px; margin-left: 10px;
} }
} }
......
import Button from './src/BasicButton.vue'; import { withInstall } from '/@/utils';
import PopConfirmButton from './src/PopConfirmButton.vue'; import button from './src/BasicButton.vue';
import popConfirmButton from './src/PopConfirmButton.vue';
export { Button, PopConfirmButton }; export const Button = withInstall(button);
export const PopConfirmButton = withInstall(popConfirmButton);
...@@ -13,11 +13,21 @@ ...@@ -13,11 +13,21 @@
import { Icon } from '/@/components/Icon'; import { Icon } from '/@/components/Icon';
const props = { const props = {
color: { type: String, validate: (v) => ['error', 'warning', 'success', ''].includes(v) }, color: { type: String, validator: (v) => ['error', 'warning', 'success', ''].includes(v) },
loading: { type: Boolean }, loading: { type: Boolean },
disabled: { type: Boolean }, disabled: { type: Boolean },
/**
* Text before icon.
*/
preIcon: { type: String }, preIcon: { type: String },
/**
* Text after icon.
*/
postIcon: { type: String }, postIcon: { type: String },
/**
* preIcon and postIcon icon size.
* @default: 14
*/
iconSize: { type: Number, default: 14 }, iconSize: { type: Number, default: 14 },
onClick: { type: Function as PropType<(...args) => any>, default: null }, onClick: { type: Function as PropType<(...args) => any>, default: null },
}; };
......
<script lang="ts"> <script lang="ts">
import { defineComponent, h, unref, computed } from 'vue'; import { defineComponent, h, unref, computed } from 'vue';
import { Popconfirm } from 'ant-design-vue';
import BasicButton from './BasicButton.vue'; import BasicButton from './BasicButton.vue';
import { Popconfirm } from 'ant-design-vue';
import { extendSlots } from '/@/utils/helper/tsxHelper'; import { extendSlots } from '/@/utils/helper/tsxHelper';
import { omit } from 'lodash-es'; import { omit } from 'lodash-es';
import { useAttrs } from '/@/hooks/core/useAttrs'; import { useAttrs } from '/@/hooks/core/useAttrs';
......
import ClickOutSide from './src/ClickOutSide.vue'; import { withInstall } from '/@/utils';
import clickOutSide from './src/ClickOutSide.vue';
export { ClickOutSide }; export const ClickOutSide = withInstall(clickOutSide);
import { install } from '/@/utils/install'; import { withInstall } from '/@/utils';
import codeEditor from './src/CodeEditor.vue'; import codeEditor from './src/CodeEditor.vue';
import jsonPreview from './src/json-preview/JsonPreview.vue'; import jsonPreview from './src/json-preview/JsonPreview.vue';
export const CodeEditor = install(codeEditor); export const CodeEditor = withInstall(codeEditor);
export const JsonPreview = install(jsonPreview); export const JsonPreview = withInstall(jsonPreview);
<template> <template>
<div class="h-full"> <div class="h-full">
<CodeMirrorEditor :value="getValue" @change="handleValueChange" :mode="mode" :readonly="readonly" /> <CodeMirrorEditor
:value="getValue"
@change="handleValueChange"
:mode="mode"
:readonly="readonly"
/>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
...@@ -13,43 +18,34 @@ ...@@ -13,43 +18,34 @@
html: 'htmlmixed', html: 'htmlmixed',
js: 'javascript', js: 'javascript',
}; };
const props = {
value: { type: [Object, String] as PropType<Record<string, any> | string> },
mode: { type: String, default: MODE.JSON },
readonly: { type: Boolean },
};
export default defineComponent({ export default defineComponent({
name: 'CodeEditor', name: 'CodeEditor',
components: { CodeMirrorEditor }, components: { CodeMirrorEditor },
props: { props,
value: {
type: [Object, String],
},
mode: {
type: String,
default: MODE.JSON,
},
readonly: {
type: Boolean,
default: false,
},
},
emits: ['change'], emits: ['change'],
setup(props, { emit }) { setup(props, { emit }) {
const getValue = computed(() => { const getValue = computed(() => {
const { value, mode } = props; const { value, mode } = props;
if (mode !== MODE.JSON) {
if (mode === MODE.JSON) { return value as string;
return isString(value)
? JSON.stringify(JSON.parse(value), null, 2)
: JSON.stringify(value, null, 2);
} }
return value; return isString(value)
? JSON.stringify(JSON.parse(value), null, 2)
: JSON.stringify(value, null, 2);
}); });
function handleValueChange(v) { function handleValueChange(v) {
emit('change', v); emit('change', v);
} }
return { return { handleValueChange, getValue };
handleValueChange,
getValue,
};
}, },
}); });
</script> </script>
...@@ -15,31 +15,25 @@ ...@@ -15,31 +15,25 @@
} from 'vue'; } from 'vue';
import { useDebounceFn } from '@vueuse/core'; import { useDebounceFn } from '@vueuse/core';
import { useAppStore } from '/@/store/modules/app'; import { useAppStore } from '/@/store/modules/app';
import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn';
import CodeMirror from 'codemirror'; import CodeMirror from 'codemirror';
// css
import './codemirror.css'; import './codemirror.css';
import 'codemirror/theme/idea.css'; import 'codemirror/theme/idea.css';
import 'codemirror/theme/material-palenight.css'; import 'codemirror/theme/material-palenight.css';
// modes // modes
import 'codemirror/mode/javascript/javascript'; import 'codemirror/mode/javascript/javascript';
import 'codemirror/mode/css/css'; import 'codemirror/mode/css/css';
import 'codemirror/mode/htmlmixed/htmlmixed'; import 'codemirror/mode/htmlmixed/htmlmixed';
const props = {
mode: { type: String, default: 'application/json' },
value: { type: String, default: '' },
readonly: { type: Boolean, default: false },
};
export default defineComponent({ export default defineComponent({
props: { props,
mode: {
type: String,
default: 'application/json',
},
value: {
type: String,
default: '',
},
readonly: {
type: Boolean,
default: false,
},
},
emits: ['change'], emits: ['change'],
setup(props, { emit }) { setup(props, { emit }) {
const el = ref(); const el = ref();
...@@ -50,11 +44,11 @@ ...@@ -50,11 +44,11 @@
watch( watch(
() => props.value, () => props.value,
async (v) => { async (value) => {
await nextTick(); await nextTick();
const oldValue = editor?.getValue(); const oldValue = editor?.getValue();
if (v !== oldValue) { if (value !== oldValue) {
editor?.setValue(v ? v : ''); editor?.setValue(value ? value : '');
} }
}, },
{ flush: 'post' } { flush: 'post' }
...@@ -113,13 +107,13 @@ ...@@ -113,13 +107,13 @@
onMounted(async () => { onMounted(async () => {
await nextTick(); await nextTick();
init(); init();
window.addEventListener('resize', debounceRefresh); useWindowSizeFn(debounceRefresh);
}); });
onUnmounted(() => { onUnmounted(() => {
window.removeEventListener('resize', debounceRefresh);
editor = null; editor = null;
}); });
return { el }; return { el };
}, },
}); });
......
...@@ -12,20 +12,21 @@ ...@@ -12,20 +12,21 @@
--qualifier: #ff6032; --qualifier: #ff6032;
--important: var(--string); --important: var(--string);
position: relative;
height: auto; height: auto;
height: 100%; height: 100%;
overflow: hidden;
font-family: var(--font-code); font-family: var(--font-code);
background: white;
direction: ltr; direction: ltr;
} }
/* PADDING */ /* PADDING */
.CodeMirror-lines { .CodeMirror-lines {
min-height: 1px; /* prevents collapsing before first draw */
padding: 4px 0; /* Vertical padding around content */ padding: 4px 0; /* Vertical padding around content */
} cursor: text;
.CodeMirror pre {
padding: 0 4px; /* Horizontal padding of content */
} }
.CodeMirror-scrollbar-filler, .CodeMirror-scrollbar-filler,
...@@ -36,6 +37,11 @@ ...@@ -36,6 +37,11 @@
/* GUTTER */ /* GUTTER */
.CodeMirror-gutters { .CodeMirror-gutters {
position: absolute;
top: 0;
left: 0;
z-index: 3;
min-height: 100%;
white-space: nowrap; white-space: nowrap;
background-color: transparent; background-color: transparent;
border-right: 1px solid #ddd; border-right: 1px solid #ddd;
...@@ -96,7 +102,9 @@ ...@@ -96,7 +102,9 @@
/* CURSOR */ /* CURSOR */
.CodeMirror-cursor { .CodeMirror-cursor {
position: absolute;
width: 0; width: 0;
pointer-events: none;
border-right: none; border-right: none;
border-left: 1px solid black; border-left: 1px solid black;
} }
...@@ -132,37 +140,19 @@ ...@@ -132,37 +140,19 @@
animation: blink 1.06s steps(1) infinite; animation: blink 1.06s steps(1) infinite;
} }
@-moz-keyframes blink { @-moz-keyframes blink {
0% {
}
50% { 50% {
background-color: transparent; background-color: transparent;
} }
100% {
}
} }
@-webkit-keyframes blink { @-webkit-keyframes blink {
0% {
}
50% { 50% {
background-color: transparent; background-color: transparent;
} }
100% {
}
} }
@keyframes blink { @keyframes blink {
0% {
}
50% { 50% {
background-color: transparent; background-color: transparent;
} }
100% {
}
} }
.cm-tab { .cm-tab {
...@@ -316,12 +306,6 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket { ...@@ -316,12 +306,6 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {
/* The rest of this file contains styles related to the mechanics of /* The rest of this file contains styles related to the mechanics of
the editor. You probably shouldn't touch them. */ the editor. You probably shouldn't touch them. */
.CodeMirror {
position: relative;
overflow: hidden;
background: white;
}
.CodeMirror-scroll { .CodeMirror-scroll {
position: relative; position: relative;
height: 100%; height: 100%;
...@@ -378,14 +362,6 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket { ...@@ -378,14 +362,6 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {
left: 0; left: 0;
} }
.CodeMirror-gutters {
position: absolute;
top: 0;
left: 0;
z-index: 3;
min-height: 100%;
}
.CodeMirror-gutter { .CodeMirror-gutter {
display: inline-block; display: inline-block;
height: 100%; height: 100%;
...@@ -422,14 +398,10 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket { ...@@ -422,14 +398,10 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {
background-color: transparent; background-color: transparent;
} }
.CodeMirror-lines {
min-height: 1px; /* prevents collapsing before first draw */
cursor: text;
}
.CodeMirror pre { .CodeMirror pre {
position: relative; position: relative;
z-index: 2; z-index: 2;
padding: 0 4px; /* Horizontal padding of content */
margin: 0; margin: 0;
overflow: visible; overflow: visible;
font-family: inherit; font-family: inherit;
...@@ -497,11 +469,6 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket { ...@@ -497,11 +469,6 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {
visibility: hidden; visibility: hidden;
} }
.CodeMirror-cursor {
position: absolute;
pointer-events: none;
}
.CodeMirror-measure pre { .CodeMirror-measure pre {
position: static; position: static;
} }
......
...@@ -8,11 +8,7 @@ ...@@ -8,11 +8,7 @@
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
export default defineComponent({ export default defineComponent({
name: 'JsonPreview', name: 'JsonPreview',
components: { components: { VueJsonPretty },
VueJsonPretty, props: { data: Object },
},
props: {
data: Object,
},
}); });
</script> </script>
import CollapseContainer from './src/collapse/CollapseContainer.vue'; import { withInstall } from '/@/utils';
import ScrollContainer from './src/ScrollContainer.vue'; import collapseContainer from './src/collapse/CollapseContainer.vue';
import LazyContainer from './src/LazyContainer.vue'; import scrollContainer from './src/ScrollContainer.vue';
import lazyContainer from './src/LazyContainer.vue';
export { CollapseContainer, ScrollContainer, LazyContainer }; export const CollapseContainer = withInstall(collapseContainer);
export * from './src/types'; export const ScrollContainer = withInstall(scrollContainer);
export const LazyContainer = withInstall(lazyContainer);
export * from './src/typing';
...@@ -18,13 +18,10 @@ ...@@ -18,13 +18,10 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import type { PropType } from 'vue'; import type { PropType } from 'vue';
import { defineComponent, reactive, onMounted, ref, toRef, toRefs } from 'vue'; import { defineComponent, reactive, onMounted, ref, toRef, toRefs } from 'vue';
import { Skeleton } from 'ant-design-vue'; import { Skeleton } from 'ant-design-vue';
import { useTimeoutFn } from '/@/hooks/core/useTimeout'; import { useTimeoutFn } from '/@/hooks/core/useTimeout';
import { useIntersectionObserver } from '/@/hooks/event/useIntersectionObserver'; import { useIntersectionObserver } from '/@/hooks/event/useIntersectionObserver';
import { propTypes } from '/@/utils/propTypes';
interface State { interface State {
isInit: boolean; isInit: boolean;
...@@ -32,36 +29,47 @@ ...@@ -32,36 +29,47 @@
intersectionObserverInstance: IntersectionObserver | null; intersectionObserverInstance: IntersectionObserver | null;
} }
const props = {
/**
* Waiting time, if the time is specified, whether visible or not, it will be automatically loaded after the specified time
*/
timeout: { type: Number },
/**
* The viewport where the component is located.
* If the component is scrolling in the page container, the viewport is the container
*/
viewport: {
type: (typeof window !== 'undefined' ? window.HTMLElement : Object) as PropType<HTMLElement>,
default: () => null,
},
/**
* Preload threshold, css unit
*/
threshold: { type: String, default: '0px' },
/**
* The scroll direction of the viewport, vertical represents the vertical direction, horizontal represents the horizontal direction
*/
direction: {
type: String,
default: 'vertical',
validator: (v) => ['vertical', 'horizontal'].includes(v),
},
/**
* The label name of the outer container that wraps the component
*/
tag: { type: String, default: 'div' },
maxWaitingTime: { type: Number, default: 80 },
/**
* transition name
*/
transitionName: { type: String, default: 'lazy-container' },
};
export default defineComponent({ export default defineComponent({
name: 'LazyContainer', name: 'LazyContainer',
components: { Skeleton }, components: { Skeleton },
inheritAttrs: false, inheritAttrs: false,
props: { props,
// Waiting time, if the time is specified, whether visible or not, it will be automatically loaded after the specified time
timeout: propTypes.number,
// The viewport where the component is located. If the component is scrolling in the page container, the viewport is the container
viewport: {
type: (typeof window !== 'undefined'
? window.HTMLElement
: Object) as PropType<HTMLElement>,
default: () => null,
},
// Preload threshold, css unit
threshold: propTypes.string.def('0px'),
// The scroll direction of the viewport, vertical represents the vertical direction, horizontal represents the horizontal direction
direction: propTypes.oneOf(['vertical', 'horizontal']).def('vertical'),
// The label name of the outer container that wraps the component
tag: propTypes.string.def('div'),
maxWaitingTime: propTypes.number.def(80),
// transition name
transitionName: propTypes.string.def('lazy-container'),
},
emits: ['init'], emits: ['init'],
setup(props, { emit }) { setup(props, { emit }) {
const elRef = ref(); const elRef = ref();
......
...@@ -7,7 +7,6 @@ ...@@ -7,7 +7,6 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, ref, unref, nextTick } from 'vue'; import { defineComponent, ref, unref, nextTick } from 'vue';
import { Scrollbar, ScrollbarType } from '/@/components/Scrollbar'; import { Scrollbar, ScrollbarType } from '/@/components/Scrollbar';
import { useScrollTo } from '/@/hooks/event/useScrollTo'; import { useScrollTo } from '/@/hooks/event/useScrollTo';
export default defineComponent({ export default defineComponent({
...@@ -16,12 +15,14 @@ ...@@ -16,12 +15,14 @@
setup() { setup() {
const scrollbarRef = ref<Nullable<ScrollbarType>>(null); const scrollbarRef = ref<Nullable<ScrollbarType>>(null);
/**
* Scroll to the specified position
*/
function scrollTo(to: number, duration = 500) { function scrollTo(to: number, duration = 500) {
const scrollbar = unref(scrollbarRef); const scrollbar = unref(scrollbarRef);
if (!scrollbar) { if (!scrollbar) {
return; return;
} }
nextTick(() => { nextTick(() => {
const wrap = unref(scrollbar.wrap); const wrap = unref(scrollbar.wrap);
if (!wrap) { if (!wrap) {
...@@ -44,12 +45,14 @@ ...@@ -44,12 +45,14 @@
return scrollbar.wrap; return scrollbar.wrap;
} }
/**
* Scroll to the bottom
*/
function scrollBottom() { function scrollBottom() {
const scrollbar = unref(scrollbarRef); const scrollbar = unref(scrollbarRef);
if (!scrollbar) { if (!scrollbar) {
return; return;
} }
nextTick(() => { nextTick(() => {
const wrap = unref(scrollbar.wrap); const wrap = unref(scrollbar.wrap);
if (!wrap) { if (!wrap) {
......
<template> <template>
<div :class="prefixCls"> <div :class="prefixCls">
<CollapseHeader <CollapseHeader v-bind="$props" :prefixCls="prefixCls" :show="show" @expand="handleExpand">
v-bind="getBindValues"
:prefixCls="prefixCls"
:show="show"
@expand="handleExpand"
>
<template #title> <template #title>
<slot name="title"></slot> <slot name="title"></slot>
</template> </template>
...@@ -16,13 +11,12 @@ ...@@ -16,13 +11,12 @@
<div class="p-2"> <div class="p-2">
<CollapseTransition :enable="canExpan"> <CollapseTransition :enable="canExpan">
<Skeleton v-if="loading" :active="active" /> <Skeleton v-if="loading" :active="loading" />
<div :class="`${prefixCls}__body`" v-else v-show="show"> <div :class="`${prefixCls}__body`" v-else v-show="show">
<slot></slot> <slot></slot>
</div> </div>
</CollapseTransition> </CollapseTransition>
</div> </div>
<div :class="`${prefixCls}__footer`" v-if="$slots.footer"> <div :class="`${prefixCls}__footer`" v-if="$slots.footer">
<slot name="footer"></slot> <slot name="footer"></slot>
</div> </div>
...@@ -30,20 +24,41 @@ ...@@ -30,20 +24,41 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import type { PropType } from 'vue'; import type { PropType } from 'vue';
import { defineComponent, ref } from 'vue';
import { defineComponent, ref, computed } from 'vue';
// component // component
import { Skeleton } from 'ant-design-vue'; import { Skeleton } from 'ant-design-vue';
import { CollapseTransition } from '/@/components/Transition'; import { CollapseTransition } from '/@/components/Transition';
import CollapseHeader from './CollapseHeader.vue'; import CollapseHeader from './CollapseHeader.vue';
import { triggerWindowResize } from '/@/utils/event'; import { triggerWindowResize } from '/@/utils/event';
// hook // hook
import { useTimeoutFn } from '/@/hooks/core/useTimeout'; import { useTimeoutFn } from '/@/hooks/core/useTimeout';
import { propTypes } from '/@/utils/propTypes';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
const props = {
title: { type: String, default: '' },
loading: { type: Boolean },
/**
* Can it be expanded
*/
canExpan: { type: Boolean, default: true },
/**
* Warm reminder on the right side of the title
*/
helpMessage: {
type: [Array, String] as PropType<string[] | string>,
default: '',
},
/**
* Whether to trigger window.resize when expanding and contracting,
* Can adapt to tables and forms, when the form shrinks, the form triggers resize to adapt to the height
*/
triggerWindowResize: { type: Boolean },
/**
* Delayed loading time
*/
lazyTime: { type: Number, default: 0 },
};
export default defineComponent({ export default defineComponent({
name: 'CollapseContainer', name: 'CollapseContainer',
components: { components: {
...@@ -51,23 +66,7 @@ ...@@ -51,23 +66,7 @@
CollapseHeader, CollapseHeader,
CollapseTransition, CollapseTransition,
}, },
props: { props,
title: propTypes.string.def(''),
// Can it be expanded
canExpan: propTypes.bool.def(true),
// Warm reminder on the right side of the title
helpMessage: {
type: [Array, String] as PropType<string[] | string>,
default: '',
},
// Whether to trigger window.resize when expanding and contracting,
// Can adapt to tables and forms, when the form shrinks, the form triggers resize to adapt to the height
triggerWindowResize: propTypes.bool,
loading: propTypes.bool.def(false),
active: propTypes.bool.def(true),
// Delayed loading time
lazyTime: propTypes.number.def(0),
},
setup(props) { setup(props) {
const show = ref(true); const show = ref(true);
...@@ -84,15 +83,10 @@ ...@@ -84,15 +83,10 @@
} }
} }
const getBindValues = computed((): any => {
return props;
});
return { return {
show, show,
handleExpand, handleExpand,
prefixCls, prefixCls,
getBindValues,
}; };
}, },
}); });
......
...@@ -8,28 +8,31 @@ ...@@ -8,28 +8,31 @@
<slot name="title"></slot> <slot name="title"></slot>
</template> </template>
</BasicTitle> </BasicTitle>
<div :class="`${prefixCls}__action`"> <div :class="`${prefixCls}__action`">
<slot name="action"></slot> <slot name="action"></slot>
<BasicArrow v-if="canExpan" top :expand="show" @click="$emit('expand')" /> <BasicArrow v-if="canExpan" up :expand="show" @click="$emit('expand')" />
</div> </div>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { BasicArrow, BasicTitle } from '/@/components/Basic'; import { BasicArrow, BasicTitle } from '/@/components/Basic';
import { propTypes } from '/@/utils/propTypes';
const props = {
prefixCls: { type: String },
helpMessage: {
type: [Array, String] as PropType<string[] | string>,
default: '',
},
title: { type: String },
show: { type: Boolean },
canExpan: { type: Boolean },
};
export default defineComponent({ export default defineComponent({
components: { BasicArrow, BasicTitle }, components: { BasicArrow, BasicTitle },
inheritAttrs: false, inheritAttrs: false,
props: { props,
prefixCls: propTypes.string,
helpMessage: propTypes.string,
title: propTypes.string,
show: propTypes.bool,
canExpan: propTypes.bool,
},
emits: ['expand'], emits: ['expand'],
}); });
</script> </script>
import { install } from '/@/utils/install'; import { withInstall } from '/@/utils';
import flowChart from './src/FlowChart.vue'; import flowChart from './src/FlowChart.vue';
export const FlowChart = install(flowChart); export const FlowChart = withInstall(flowChart);
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
v-if="showAdvancedButton && !hideAdvanceBtn" v-if="showAdvancedButton && !hideAdvanceBtn"
> >
{{ isAdvanced ? t('component.form.putAway') : t('component.form.unfold') }} {{ isAdvanced ? t('component.form.putAway') : t('component.form.unfold') }}
<BasicArrow class="ml-1" :expand="!isAdvanced" top /> <BasicArrow class="ml-1" :expand="!isAdvanced" up />
</Button> </Button>
<slot name="advanceAfter"></slot> <slot name="advanceAfter"></slot>
</FormItem> </FormItem>
...@@ -99,16 +99,14 @@ ...@@ -99,16 +99,14 @@
return actionColOpt; return actionColOpt;
}); });
const getResetBtnOptions = computed( const getResetBtnOptions = computed((): ButtonOptions => {
(): ButtonOptions => { return Object.assign(
return Object.assign( {
{ text: t('common.resetText'),
text: t('common.resetText'), },
}, props.resetButtonOptions
props.resetButtonOptions );
); });
}
);
const getSubmitBtnOptions = computed(() => { const getSubmitBtnOptions = computed(() => {
return Object.assign( return Object.assign(
......
import type { RouteLocationNormalized, RouteRecordNormalized } from 'vue-router'; import type { RouteLocationNormalized, RouteRecordNormalized } from 'vue-router';
import type { App, Plugin } from 'vue';
import { unref } from 'vue'; import { unref } from 'vue';
import { isObject } from '/@/utils/is'; import { isObject } from '/@/utils/is';
...@@ -76,3 +78,14 @@ export function getRawRoute(route: RouteLocationNormalized): RouteLocationNormal ...@@ -76,3 +78,14 @@ export function getRawRoute(route: RouteLocationNormalized): RouteLocationNormal
: undefined) as RouteRecordNormalized[], : undefined) as RouteRecordNormalized[],
}; };
} }
export const withInstall = <T>(component: T, alias?: string) => {
const comp = component as any;
comp.install = (app: App) => {
app.component(comp.name || comp.displayName, component);
if (alias) {
app.config.globalProperties[alias] = component;
}
};
return component as T & Plugin;
};
import { App, Plugin } from 'vue';
export const install = <T>(component: T, alias?: string) => {
const C = component as any;
C.install = (app: App) => {
app.component(C.name, component);
if (alias) {
app.config.globalProperties[alias] = component;
}
};
return component as T & Plugin;
};
...@@ -35,8 +35,7 @@ ...@@ -35,8 +35,7 @@
class=" class="
my-auto my-auto
mx-auto mx-auto
xl:ml-20 xl:ml-20 xl:bg-transparent
xl:bg-transparent
px-5 px-5
py-8 py-8
sm:px-8 sm:px-8
......
declare module '*.vue' { declare module '*.vue' {
import { defineComponent } from 'vue'; import { DefineComponent } from 'vue';
const Component: ReturnType<typeof defineComponent>; const Component: DefineComponent<{}, {}, any>;
export default Component; export default Component;
} }
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论