style: lint格式化文件

This commit is contained in:
2025-10-21 18:04:02 +08:00
parent f9ca969fec
commit 7629120548
1092 changed files with 148218 additions and 157907 deletions

View File

@ -1,76 +1,76 @@
<template>
<div v-if="getShowDarkModeToggle" :class="getClass" @click="toggleDarkMode">
<div :class="`${prefixCls}-inner`"></div>
<SvgIcon size="14" name="sun" />
<SvgIcon size="14" name="moon" />
</div>
<div v-if="getShowDarkModeToggle" :class="getClass" @click="toggleDarkMode">
<div :class="`${prefixCls}-inner`"></div>
<SvgIcon size="14" name="sun" />
<SvgIcon size="14" name="moon" />
</div>
</template>
<script lang="ts" setup>
import { computed, unref } from 'vue';
import { SvgIcon } from '/@/components/Icon';
import { useDesign } from '/@/hooks/web/useDesign';
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
import { updateHeaderBgColor, updateSidebarBgColor } from '/@/logics/theme/updateBackground';
import { updateDarkTheme } from '/@/logics/theme/dark';
import { ThemeEnum } from '/@/enums/appEnum';
import { computed, unref } from 'vue';
import { SvgIcon } from '/@/components/Icon';
import { useDesign } from '/@/hooks/web/useDesign';
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
import { updateHeaderBgColor, updateSidebarBgColor } from '/@/logics/theme/updateBackground';
import { updateDarkTheme } from '/@/logics/theme/dark';
import { ThemeEnum } from '/@/enums/appEnum';
const { prefixCls } = useDesign('dark-switch');
const { getDarkMode, setDarkMode, getShowDarkModeToggle } = useRootSetting();
const { prefixCls } = useDesign('dark-switch');
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`]: unref(isDark),
},
]);
const getClass = computed(() => [
prefixCls,
{
[`${prefixCls}--dark`]: unref(isDark)
}
]);
function toggleDarkMode() {
const darkMode = getDarkMode.value === ThemeEnum.DARK ? ThemeEnum.LIGHT : ThemeEnum.DARK;
setDarkMode(darkMode);
updateDarkTheme(darkMode);
updateHeaderBgColor();
updateSidebarBgColor();
}
function toggleDarkMode() {
const darkMode = getDarkMode.value === ThemeEnum.DARK ? ThemeEnum.LIGHT : ThemeEnum.DARK;
setDarkMode(darkMode);
updateDarkTheme(darkMode);
updateHeaderBgColor();
updateSidebarBgColor();
}
</script>
<style lang="less" scoped>
@prefix-cls: ~'@{namespace}-dark-switch';
@prefix-cls: ~'@{namespace}-dark-switch';
html[data-theme='dark'] {
.@{prefix-cls} {
border: 1px solid rgb(196 188 188);
}
}
html[data-theme='dark'] {
.@{prefix-cls} {
border: 1px solid rgb(196 188 188);
}
}
position: relative;
display: flex;
width: 50px;
height: 26px;
padding: 0 6px;
margin-left: auto;
cursor: pointer;
background-color: #151515;
border-radius: 30px;
justify-content: space-between;
align-items: center;
.@{prefix-cls} {
position: relative;
display: flex;
width: 50px;
height: 26px;
padding: 0 6px;
margin-left: auto;
cursor: pointer;
background-color: #151515;
border-radius: 30px;
justify-content: space-between;
align-items: center;
&-inner {
position: absolute;
z-index: 1;
width: 18px;
height: 18px;
background-color: #fff;
border-radius: 50%;
transition: transform 0.5s, background-color 0.5s;
will-change: transform;
}
&-inner {
position: absolute;
z-index: 1;
width: 18px;
height: 18px;
background-color: #fff;
border-radius: 50%;
transition: transform 0.5s, background-color 0.5s;
will-change: transform;
&--dark {
.@{prefix-cls}-inner {
transform: translateX(calc(100% + 2px));
}
}
}
&--dark {
.@{prefix-cls}-inner {
transform: translateX(calc(100% + 2px));
}
}
}
</style>

View File

@ -3,87 +3,80 @@
* @Description: Multi-language switching component
-->
<template>
<Dropdown
placement="bottom"
:trigger="['click']"
:dropMenuList="languageTypeList"
:selectedKeys="selectedKeys"
@menu-event="handleMenuEvent"
overlayClassName="app-locale-picker-overlay"
>
<span class="cursor-pointer flex items-center">
<Icon icon="ri:translate" size="24" />
<span v-if="showText" class="ml-1">{{ getLocaleText }}</span>
</span>
</Dropdown>
<Dropdown placement="bottom" :trigger="['click']" :dropMenuList="languageTypeList" :selectedKeys="selectedKeys" @menu-event="handleMenuEvent" overlayClassName="app-locale-picker-overlay">
<span class="cursor-pointer flex items-center">
<Icon icon="ri:translate" size="24" />
<span v-if="showText" class="ml-1">{{ getLocaleText }}</span>
</span>
</Dropdown>
</template>
<script lang="ts" setup>
import type { LocaleType } from '/#/config';
import type { DropMenu } from '/@/components/Dropdown';
import { ref, watchEffect, unref, computed, onMounted } from 'vue';
import { Dropdown } from '/@/components/Dropdown';
import { Icon } from '/@/components/Icon';
import { useLocale } from '/@/locales/useLocale';
// import { localeList } from '/@/settings/localeSetting';
import { getLanguageTypeList } from '/@/api/system/language';
import type { LocaleType } from '/#/config';
import type { DropMenu } from '/@/components/Dropdown';
import { ref, watchEffect, unref, computed, onMounted } from 'vue';
import { Dropdown } from '/@/components/Dropdown';
import { Icon } from '/@/components/Icon';
import { useLocale } from '/@/locales/useLocale';
// import { localeList } from '/@/settings/localeSetting';
import { getLanguageTypeList } from '/@/api/system/language';
const props = defineProps({
/**
* Whether to display text
*/
showText: { type: Boolean, default: true },
/**
* Whether to refresh the interface when changing
*/
reload: { type: Boolean },
});
const props = defineProps({
/**
* Whether to display text
*/
showText: { type: Boolean, default: true },
/**
* Whether to refresh the interface when changing
*/
reload: { type: Boolean }
});
const selectedKeys = ref<string[]>([]);
const selectedKeys = ref<string[]>([]);
const languageTypeList = ref<DropMenu[]>([]);
const languageTypeList = ref<DropMenu[]>([]);
onMounted(async () => {
const typeList = await getLanguageTypeList();
for (const item of typeList) {
languageTypeList.value.push({
text: item.name,
event: item.code,
} as DropMenu);
onMounted(async () => {
const typeList = await getLanguageTypeList();
for (const item of typeList) {
languageTypeList.value.push({
text: item.name,
event: item.code
} as DropMenu);
}
});
const { changeLocale, getLocale } = useLocale();
const getLocaleText = computed(() => {
const key = selectedKeys.value[0];
if (!key) {
return '';
}
return languageTypeList.value.find((item) => item.event === key)?.text;
});
watchEffect(() => {
selectedKeys.value = [unref(getLocale)];
});
async function toggleLocale(lang: LocaleType | string) {
await changeLocale(lang as LocaleType);
selectedKeys.value = [lang as string];
props.reload && location.reload();
}
});
const { changeLocale, getLocale } = useLocale();
const getLocaleText = computed(() => {
const key = selectedKeys.value[0];
if (!key) {
return '';
function handleMenuEvent(menu: DropMenu) {
if (unref(getLocale) === menu.event) {
return;
}
toggleLocale(menu.event as string);
}
return languageTypeList.value.find((item) => item.event === key)?.text;
});
watchEffect(() => {
selectedKeys.value = [unref(getLocale)];
});
async function toggleLocale(lang: LocaleType | string) {
await changeLocale(lang as LocaleType);
selectedKeys.value = [lang as string];
props.reload && location.reload();
}
function handleMenuEvent(menu: DropMenu) {
if (unref(getLocale) === menu.event) {
return;
}
toggleLocale(menu.event as string);
}
</script>
<style lang="less">
.app-locale-picker-overlay {
.ant-dropdown-menu-item {
min-width: 160px;
.app-locale-picker-overlay {
.ant-dropdown-menu-item {
min-width: 160px;
}
}
}
</style>

View File

@ -1,82 +1,77 @@
<script lang="ts">
import { defineComponent, toRefs, ref, unref } from 'vue';
import { createAppProviderContext } from './useAppContext';
import { createBreakpointListen } from '/@/hooks/event/useBreakpoint';
import { prefixCls } from '/@/settings/designSetting';
import { useAppStore } from '/@/store/modules/app';
import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
import { defineComponent, toRefs, ref, unref } from 'vue';
import { createAppProviderContext } from './useAppContext';
import { createBreakpointListen } from '/@/hooks/event/useBreakpoint';
import { prefixCls } from '/@/settings/designSetting';
import { useAppStore } from '/@/store/modules/app';
import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
const props = {
/**
* class style prefix
*/
prefixCls: { type: String, default: prefixCls },
};
const props = {
/**
* class style prefix
*/
prefixCls: { type: String, default: prefixCls }
};
export default defineComponent({
name: 'AppProvider',
inheritAttrs: false,
props,
setup(props, { slots }) {
const isMobile = ref(false);
const isSetState = ref(false);
export default defineComponent({
name: 'AppProvider',
inheritAttrs: false,
props,
setup(props, { slots }) {
const isMobile = ref(false);
const isSetState = ref(false);
const appStore = useAppStore();
const appStore = useAppStore();
// Monitor screen breakpoint information changes
createBreakpointListen(({ screenMap, sizeEnum, width }) => {
const lgWidth = screenMap.get(sizeEnum.LG);
if (lgWidth) {
isMobile.value = width.value - 1 < lgWidth;
}
handleRestoreState();
});
const { prefixCls } = toRefs(props);
// Inject variables into the global
createAppProviderContext({ prefixCls, isMobile });
/**
* Used to maintain the state before the window changes
*/
function handleRestoreState() {
if (unref(isMobile)) {
if (!unref(isSetState)) {
isSetState.value = true;
const {
menuSetting: {
type: menuType,
mode: menuMode,
collapsed: menuCollapsed,
split: menuSplit,
},
} = appStore.getProjectConfig;
appStore.setProjectConfig({
menuSetting: {
type: MenuTypeEnum.SIDEBAR,
mode: MenuModeEnum.INLINE,
split: false,
},
// Monitor screen breakpoint information changes
createBreakpointListen(({ screenMap, sizeEnum, width }) => {
const lgWidth = screenMap.get(sizeEnum.LG);
if (lgWidth) {
isMobile.value = width.value - 1 < lgWidth;
}
handleRestoreState();
});
appStore.setBeforeMiniInfo({ menuMode, menuCollapsed, menuType, menuSplit });
}
} else {
if (unref(isSetState)) {
isSetState.value = false;
const { menuMode, menuCollapsed, menuType, menuSplit } = appStore.getBeforeMiniInfo;
appStore.setProjectConfig({
menuSetting: {
type: menuType,
mode: menuMode,
collapsed: menuCollapsed,
split: menuSplit,
},
});
}
const { prefixCls } = toRefs(props);
// Inject variables into the global
createAppProviderContext({ prefixCls, isMobile });
/**
* Used to maintain the state before the window changes
*/
function handleRestoreState() {
if (unref(isMobile)) {
if (!unref(isSetState)) {
isSetState.value = true;
const {
menuSetting: { type: menuType, mode: menuMode, collapsed: menuCollapsed, split: menuSplit }
} = appStore.getProjectConfig;
appStore.setProjectConfig({
menuSetting: {
type: MenuTypeEnum.SIDEBAR,
mode: MenuModeEnum.INLINE,
split: false
}
});
appStore.setBeforeMiniInfo({ menuMode, menuCollapsed, menuType, menuSplit });
}
} else {
if (unref(isSetState)) {
isSetState.value = false;
const { menuMode, menuCollapsed, menuType, menuSplit } = appStore.getBeforeMiniInfo;
appStore.setProjectConfig({
menuSetting: {
type: menuType,
mode: menuMode,
collapsed: menuCollapsed,
split: menuSplit
}
});
}
}
}
return () => slots.default?.();
}
}
return () => slots.default?.();
},
});
});
</script>

View File

@ -3,84 +3,77 @@
* @Description: post switching component
-->
<template>
<Tooltip :title="t('岗位切换')" placement="bottom" :mouseEnterDelay="0.5">
<Dropdown
placement="bottom"
:trigger="['click']"
:dropMenuList="formInfo.posts"
:selectedKeys="selectedKeys"
@menu-event="handleMenuEvent"
overlayClassName="app-locale-picker-overlay"
>
<span class="cursor-pointer flex items-center p-2">
<Icon icon="ant-design:user-switch-outlined" size="24" />
</span>
</Dropdown>
</Tooltip>
<Tooltip :title="t('岗位切换')" placement="bottom" :mouseEnterDelay="0.5">
<Dropdown placement="bottom" :trigger="['click']" :dropMenuList="formInfo.posts" :selectedKeys="selectedKeys" @menu-event="handleMenuEvent" overlayClassName="app-locale-picker-overlay">
<span class="cursor-pointer flex items-center p-2">
<Icon icon="ant-design:user-switch-outlined" size="24" />
</span>
</Dropdown>
</Tooltip>
</template>
<script lang="ts" setup>
import type { DropMenu } from '/@/components/Dropdown';
import { ref, watchEffect, watch } from 'vue';
import { Dropdown } from '/@/components/Dropdown';
import { Icon } from '/@/components/Icon';
import { storeToRefs } from 'pinia';
import { Tooltip } from 'ant-design-vue';
import { useUserStore } from '/@/store/modules/user';
import { changePost } from '/@/api/system/post';
import { useI18n } from '/@/hooks/web/useI18n';
import type { DropMenu } from '/@/components/Dropdown';
import { ref, watchEffect, watch } from 'vue';
import { Dropdown } from '/@/components/Dropdown';
import { Icon } from '/@/components/Icon';
import { storeToRefs } from 'pinia';
import { Tooltip } from 'ant-design-vue';
import { useUserStore } from '/@/store/modules/user';
import { changePost } from '/@/api/system/post';
import { useI18n } from '/@/hooks/web/useI18n';
const userStore = useUserStore();
const { t } = useI18n();
const { userInfo } = storeToRefs(userStore);
const formInfo: any = ref({
posts: [],
postId: '',
});
getDropMenuList();
watch(
() => userInfo.value,
(val) => {
if (val) getDropMenuList();
},
);
const selectedKeys = ref<string[]>([]);
const userStore = useUserStore();
const { t } = useI18n();
const { userInfo } = storeToRefs(userStore);
const formInfo: any = ref({
posts: [],
postId: ''
});
getDropMenuList();
watch(
() => userInfo.value,
(val) => {
if (val) getDropMenuList();
}
);
const selectedKeys = ref<string[]>([]);
watchEffect(() => {
selectedKeys.value = [formInfo.value.postId];
});
function getDropMenuList() {
if (userInfo.value?.posts) {
formInfo.value = userInfo.value;
formInfo.value.posts.forEach((o) => {
o.text = o.name;
o.event = o.id;
});
watchEffect(() => {
selectedKeys.value = [formInfo.value.postId];
});
function getDropMenuList() {
if (userInfo.value?.posts) {
formInfo.value = userInfo.value;
formInfo.value.posts.forEach((o) => {
o.text = o.name;
o.event = o.id;
});
}
}
}
async function toggleLocale(lang: string) {
let res = await changePost(lang);
async function toggleLocale(lang: string) {
let res = await changePost(lang);
selectedKeys.value = [lang as string];
formInfo.value.departmentId = res.departmentId;
formInfo.value.departmentName = res.departmentName;
formInfo.value.postId = res.postId;
formInfo.value.postName = res.postName;
selectedKeys.value = [lang as string];
formInfo.value.departmentId = res.departmentId;
formInfo.value.departmentName = res.departmentName;
formInfo.value.postId = res.postId;
formInfo.value.postName = res.postName;
userStore.setUserInfo(formInfo.value);
}
function handleMenuEvent(menu: DropMenu) {
if (formInfo.value.postId === menu.event) {
return;
userStore.setUserInfo(formInfo.value);
}
function handleMenuEvent(menu: DropMenu) {
if (formInfo.value.postId === menu.event) {
return;
}
toggleLocale(menu.event as string);
}
toggleLocale(menu.event as string);
}
</script>
<style lang="less">
.app-locale-picker-overlay {
.ant-dropdown-menu-item {
min-width: 160px;
.app-locale-picker-overlay {
.ant-dropdown-menu-item {
min-width: 160px;
}
}
}
</style>

View File

@ -1,98 +1,90 @@
<template>
<Tooltip placement="bottom" :mouseEnterDelay="0.5">
<Dropdown v-if="formInfo.tenants.length>1"
placement="bottom"
:trigger="['click']"
:dropMenuList="formInfo.tenants"
:selectedKeys="selectedKeys"
@menu-event="handleMenuEvent"
overlayClassName="app-locale-picker-overlay"
maxTextWidth=200
>
<span class="cursor-pointer flex items-center" style="margin-left: 10px;">
<Icon icon="ant-design:apartment-outlined" size="18" style="color:#222222"/>
<span style="color: rgba(0, 0, 0, 0.85);margin-left: 5px;">{{formInfo.tenantName}}</span>
</span>
</Dropdown>
<span v-else class="flex items-center" style="margin-left: 10px;">
<Icon icon="ant-design:apartment-outlined" size="18" style="color:#222222"/>
<span style="color: rgba(0, 0, 0, 0.85);margin-left: 5px;">{{formInfo.tenantName}}</span>
</span>
</Tooltip>
<Tooltip placement="bottom" :mouseEnterDelay="0.5">
<Dropdown v-if="formInfo.tenants.length > 1" placement="bottom" :trigger="['click']" :dropMenuList="formInfo.tenants" :selectedKeys="selectedKeys" @menu-event="handleMenuEvent" overlayClassName="app-locale-picker-overlay" maxTextWidth="200">
<span class="cursor-pointer flex items-center" style="margin-left: 10px">
<Icon icon="ant-design:apartment-outlined" size="18" style="color: #222222" />
<span style="color: rgba(0, 0, 0, 0.85); margin-left: 5px">{{ formInfo.tenantName }}</span>
</span>
</Dropdown>
<span v-else class="flex items-center" style="margin-left: 10px">
<Icon icon="ant-design:apartment-outlined" size="18" style="color: #222222" />
<span style="color: rgba(0, 0, 0, 0.85); margin-left: 5px">{{ formInfo.tenantName }}</span>
</span>
</Tooltip>
</template>
<script lang="ts" setup>
import type { DropMenu } from '/@/components/Dropdown';
import { ref, watchEffect, watch, h } from 'vue';
import { Dropdown } from '/@/components/Dropdown';
import { Icon } from '/@/components/Icon';
import { storeToRefs } from 'pinia';
import { Tooltip } from 'ant-design-vue';
import { useUserStore } from '/@/store/modules/user';
import { useI18n } from '/@/hooks/web/useI18n';
import {useMessage} from "/@/hooks/web/useMessage";
import {useTenantManager} from "/@/utils/tenantManager";
import type { DropMenu } from '/@/components/Dropdown';
import { ref, watchEffect, watch, h } from 'vue';
import { Dropdown } from '/@/components/Dropdown';
import { Icon } from '/@/components/Icon';
import { storeToRefs } from 'pinia';
import { Tooltip } from 'ant-design-vue';
import { useUserStore } from '/@/store/modules/user';
import { useI18n } from '/@/hooks/web/useI18n';
import { useMessage } from '/@/hooks/web/useMessage';
import { useTenantManager } from '/@/utils/tenantManager';
const userStore = useUserStore();
const { t } = useI18n();
const { userInfo } = storeToRefs(userStore);
const formInfo: any = ref({
tenants: [],
tenantId: '',
tenantCode: '',
tenantName: '',
});
const {toggleLocal}=useTenantManager();
getDropMenuList();
watch(
() => userInfo.value,
(val) => {
if (val) getDropMenuList();
},
);
const selectedKeys = ref<string[]>([]);
watchEffect(() => {
selectedKeys.value = [formInfo.value.tenantCode];
});
function getDropMenuList() {
if (userInfo.value?.tenants) {
formInfo.value = userInfo.value;
formInfo.value.tenants.forEach((o) => {
o.text = o.name;
o.event = o.code;
if(formInfo.value.tenantCode==o.code){
o.icon="ant-design:check-outlined";
const userStore = useUserStore();
const { t } = useI18n();
const { userInfo } = storeToRefs(userStore);
const formInfo: any = ref({
tenants: [],
tenantId: '',
tenantCode: '',
tenantName: ''
});
const { toggleLocal } = useTenantManager();
getDropMenuList();
watch(
() => userInfo.value,
(val) => {
if (val) getDropMenuList();
}
});
}
}
);
const selectedKeys = ref<string[]>([]);
async function switchTenant(tenantCode: string) {
await toggleLocal({tenantCode:tenantCode, goHome:true,tabCloseAction:"closeAll"});
}
function handleMenuEvent(menu: DropMenu) {
if (formInfo.value.tenantCode === menu.event) {
return;
watchEffect(() => {
selectedKeys.value = [formInfo.value.tenantCode];
});
function getDropMenuList() {
if (userInfo.value?.tenants) {
formInfo.value = userInfo.value;
formInfo.value.tenants.forEach((o) => {
o.text = o.name;
o.event = o.code;
if (formInfo.value.tenantCode == o.code) {
o.icon = 'ant-design:check-outlined';
}
});
}
}
async function switchTenant(tenantCode: string) {
await toggleLocal({ tenantCode: tenantCode, goHome: true, tabCloseAction: 'closeAll' });
}
function handleMenuEvent(menu: DropMenu) {
if (formInfo.value.tenantCode === menu.event) {
return;
}
const { createConfirm } = useMessage();
createConfirm({
iconType: 'warning',
title: () => h('span', t('温馨提醒')),
content: () => h('span', t('是否确认切换租户?未保存的数据可能会丢失!')),
onOk: async () => {
switchTenant(menu.event as string);
},
okText: () => t('确认'),
cancelText: () => t('取消')
});
}
const { createConfirm } = useMessage();
createConfirm({
iconType: 'warning',
title: () => h('span', t('温馨提醒')),
content: () => h('span', t('是否确认切换租户?未保存的数据可能会丢失!')),
onOk: async () => {
switchTenant(menu.event as string);
},
okText: () => t('确认'),
cancelText: () => t('取消'),
});
}
</script>
<style lang="less">
.app-locale-picker-overlay {
.ant-dropdown-menu-item {
min-width: 160px;
.app-locale-picker-overlay {
.ant-dropdown-menu-item {
min-width: 160px;
}
}
}
</style>

View File

@ -1,33 +1,33 @@
<script lang="tsx">
import { defineComponent, ref, unref } from 'vue';
import { Tooltip } from 'ant-design-vue';
import { Icon } from '/@/components/Icon';
import AppSearchModal from './AppSearchModal.vue';
import { useI18n } from '/@/hooks/web/useI18n';
import { defineComponent, ref, unref } from 'vue';
import { Tooltip } from 'ant-design-vue';
import { Icon } from '/@/components/Icon';
import AppSearchModal from './AppSearchModal.vue';
import { useI18n } from '/@/hooks/web/useI18n';
export default defineComponent({
name: 'AppSearch',
setup() {
const showModal = ref(false);
const { t } = useI18n();
export default defineComponent({
name: 'AppSearch',
setup() {
const showModal = ref(false);
const { t } = useI18n();
function changeModal(show: boolean) {
showModal.value = show;
}
function changeModal(show: boolean) {
showModal.value = show;
}
return () => {
return (
<div class="p-1" onClick={changeModal.bind(null, true)}>
<Tooltip>
{{
title: () => t('搜索'),
default: () => <Icon size="20" icon="fa:search" />,
}}
</Tooltip>
<AppSearchModal onClose={changeModal.bind(null, false)} visible={unref(showModal)} />
</div>
);
};
},
});
return () => {
return (
<div class="p-1" onClick={changeModal.bind(null, true)}>
<Tooltip>
{{
title: () => t('搜索'),
default: () => <Icon size="20" icon="fa:search" />
}}
</Tooltip>
<AppSearchModal onClose={changeModal.bind(null, false)} visible={unref(showModal)} />
</div>
);
};
}
});
</script>

View File

@ -1,56 +1,55 @@
<template>
<div :class="`${prefixCls}`">
<AppSearchKeyItem :class="`${prefixCls}-item`" icon="ant-design:enter-outlined" />
<span>{{ t('确认') }}</span>
<AppSearchKeyItem :class="`${prefixCls}-item`" icon="ion:arrow-up-outline" />
<AppSearchKeyItem :class="`${prefixCls}-item`" icon="ion:arrow-down-outline" />
<span>{{ t('切换') }}</span>
<AppSearchKeyItem :class="`${prefixCls}-item`" icon="mdi:keyboard-esc" />
<span>{{ t('关闭') }}</span>
</div>
<div :class="`${prefixCls}`">
<AppSearchKeyItem :class="`${prefixCls}-item`" icon="ant-design:enter-outlined" />
<span>{{ t('确认') }}</span>
<AppSearchKeyItem :class="`${prefixCls}-item`" icon="ion:arrow-up-outline" />
<AppSearchKeyItem :class="`${prefixCls}-item`" icon="ion:arrow-down-outline" />
<span>{{ t('切换') }}</span>
<AppSearchKeyItem :class="`${prefixCls}-item`" icon="mdi:keyboard-esc" />
<span>{{ t('关闭') }}</span>
</div>
</template>
<script lang="ts" setup>
import AppSearchKeyItem from './AppSearchKeyItem.vue';
import { useDesign } from '/@/hooks/web/useDesign';
import { useI18n } from '/@/hooks/web/useI18n';
const { prefixCls } = useDesign('app-search-footer');
const { t } = useI18n();
import AppSearchKeyItem from './AppSearchKeyItem.vue';
import { useDesign } from '/@/hooks/web/useDesign';
import { useI18n } from '/@/hooks/web/useI18n';
const { prefixCls } = useDesign('app-search-footer');
const { t } = useI18n();
</script>
<style lang="less" scoped>
@prefix-cls: ~'@{namespace}-app-search-footer';
@prefix-cls: ~'@{namespace}-app-search-footer';
.@{prefix-cls} {
position: relative;
display: flex;
height: 44px;
padding: 0 16px;
font-size: 12px;
color: #666;
background-color: @component-background;
border-top: 1px solid @border-color-base;
border-radius: 0 0 16px 16px;
align-items: center;
flex-shrink: 0;
.@{prefix-cls} {
position: relative;
display: flex;
height: 44px;
padding: 0 16px;
font-size: 12px;
color: #666;
background-color: @component-background;
border-top: 1px solid @border-color-base;
border-radius: 0 0 16px 16px;
align-items: center;
flex-shrink: 0;
&-item {
display: flex;
width: 20px;
height: 18px;
padding-bottom: 2px;
margin-right: 0.4em;
background-color: linear-gradient(-225deg, #d5dbe4, #f8f8f8);
border-radius: 2px;
box-shadow: inset 0 -2px 0 0 #cdcde6, inset 0 0 1px 1px #fff,
0 1px 2px 1px rgb(30 35 90 / 40%);
align-items: center;
justify-content: center;
&-item {
display: flex;
width: 20px;
height: 18px;
padding-bottom: 2px;
margin-right: 0.4em;
background-color: linear-gradient(-225deg, #d5dbe4, #f8f8f8);
border-radius: 2px;
box-shadow: inset 0 -2px 0 0 #cdcde6, inset 0 0 1px 1px #fff, 0 1px 2px 1px rgb(30 35 90 / 40%);
align-items: center;
justify-content: center;
&:nth-child(2),
&:nth-child(3),
&:nth-child(6) {
margin-left: 14px;
}
&:nth-child(2),
&:nth-child(3),
&:nth-child(6) {
margin-left: 14px;
}
}
}
}
</style>

View File

@ -1,11 +1,11 @@
<template>
<span :class="$attrs.class">
<Icon :icon="icon" />
</span>
<span :class="$attrs.class">
<Icon :icon="icon" />
</span>
</template>
<script lang="ts" setup>
import { Icon } from '/@/components/Icon';
defineProps({
icon: String,
});
import { Icon } from '/@/components/Icon';
defineProps({
icon: String
});
</script>

View File

@ -1,267 +1,260 @@
<template>
<Teleport to="body">
<transition name="zoom-fade" mode="out-in">
<div :class="getClass" @click.stop v-if="visible">
<div :class="`${prefixCls}-content`" v-click-outside="handleClose">
<div :class="`${prefixCls}-input__wrapper`">
<a-input
:class="`${prefixCls}-input`"
:placeholder="t('搜索')"
ref="inputRef"
allow-clear
@change="handleSearch"
>
<template #prefix>
<SearchOutlined />
</template>
</a-input>
<span :class="`${prefixCls}-cancel`" @click="handleClose">
{{ t('取消') }}
</span>
</div>
<Teleport to="body">
<transition name="zoom-fade" mode="out-in">
<div :class="getClass" @click.stop v-if="visible">
<div :class="`${prefixCls}-content`" v-click-outside="handleClose">
<div :class="`${prefixCls}-input__wrapper`">
<a-input :class="`${prefixCls}-input`" :placeholder="t('搜索')" ref="inputRef" allow-clear @change="handleSearch">
<template #prefix>
<SearchOutlined />
</template>
</a-input>
<span :class="`${prefixCls}-cancel`" @click="handleClose">
{{ t('取消') }}
</span>
</div>
<div :class="`${prefixCls}-not-data`" v-show="getIsNotData">
{{ t('暂无搜索结果') }}
</div>
<div :class="`${prefixCls}-not-data`" v-show="getIsNotData">
{{ t('暂无搜索结果') }}
</div>
<ul :class="`${prefixCls}-list`" v-show="!getIsNotData" ref="scrollWrap">
<li
:ref="setRefs(index)"
v-for="(item, index) in searchResult"
:key="item.path"
:data-index="index"
@mouseenter="handleMouseenter"
@click="handleEnter"
:class="[
`${prefixCls}-list__item`,
{
[`${prefixCls}-list__item--active`]: activeIndex === index,
},
]"
>
<div :class="`${prefixCls}-list__item-icon`">
<Icon :icon="item.icon || 'mdi:form-select'" :size="20" />
</div>
<div :class="`${prefixCls}-list__item-text`">
{{ item.name }}
</div>
<div :class="`${prefixCls}-list__item-enter`">
<Icon icon="ant-design:enter-outlined" :size="20" />
</div>
</li>
</ul>
<AppSearchFooter />
</div>
</div>
</transition>
</Teleport>
<ul :class="`${prefixCls}-list`" v-show="!getIsNotData" ref="scrollWrap">
<li
:ref="setRefs(index)"
v-for="(item, index) in searchResult"
:key="item.path"
:data-index="index"
@mouseenter="handleMouseenter"
@click="handleEnter"
:class="[
`${prefixCls}-list__item`,
{
[`${prefixCls}-list__item--active`]: activeIndex === index
}
]"
>
<div :class="`${prefixCls}-list__item-icon`">
<Icon :icon="item.icon || 'mdi:form-select'" :size="20" />
</div>
<div :class="`${prefixCls}-list__item-text`">
{{ item.name }}
</div>
<div :class="`${prefixCls}-list__item-enter`">
<Icon icon="ant-design:enter-outlined" :size="20" />
</div>
</li>
</ul>
<AppSearchFooter />
</div>
</div>
</transition>
</Teleport>
</template>
<script lang="ts" setup>
import { computed, unref, ref, watch, nextTick } from 'vue';
import { SearchOutlined } from '@ant-design/icons-vue';
import AppSearchFooter from './AppSearchFooter.vue';
import Icon from '/@/components/Icon';
// @ts-ignore
import vClickOutside from '/@/directives/clickOutside';
import { useDesign } from '/@/hooks/web/useDesign';
import { useRefs } from '/@/hooks/core/useRefs';
import { useMenuSearch } from './useMenuSearch';
import { useI18n } from '/@/hooks/web/useI18n';
import { useAppInject } from '/@/hooks/web/useAppInject';
import { computed, unref, ref, watch, nextTick } from 'vue';
import { SearchOutlined } from '@ant-design/icons-vue';
import AppSearchFooter from './AppSearchFooter.vue';
import Icon from '/@/components/Icon';
// @ts-ignore
import vClickOutside from '/@/directives/clickOutside';
import { useDesign } from '/@/hooks/web/useDesign';
import { useRefs } from '/@/hooks/core/useRefs';
import { useMenuSearch } from './useMenuSearch';
import { useI18n } from '/@/hooks/web/useI18n';
import { useAppInject } from '/@/hooks/web/useAppInject';
const props = defineProps({
visible: { type: Boolean },
});
const props = defineProps({
visible: { type: Boolean }
});
const emit = defineEmits(['close']);
const emit = defineEmits(['close']);
const scrollWrap = ref(null);
const inputRef = ref<Nullable<HTMLElement>>(null);
const scrollWrap = ref(null);
const inputRef = ref<Nullable<HTMLElement>>(null);
const { t } = useI18n();
const { prefixCls } = useDesign('app-search-modal');
const [refs, setRefs] = useRefs();
const { getIsMobile } = useAppInject();
const { t } = useI18n();
const { prefixCls } = useDesign('app-search-modal');
const [refs, setRefs] = useRefs();
const { getIsMobile } = useAppInject();
const { handleSearch, searchResult, keyword, activeIndex, handleEnter, handleMouseenter } =
useMenuSearch(refs, scrollWrap, emit);
const { handleSearch, searchResult, keyword, activeIndex, handleEnter, handleMouseenter } = useMenuSearch(refs, scrollWrap, emit);
const getIsNotData = computed(() => !keyword || unref(searchResult).length === 0);
const getIsNotData = computed(() => !keyword || unref(searchResult).length === 0);
const getClass = computed(() => {
return [
prefixCls,
{
[`${prefixCls}--mobile`]: unref(getIsMobile),
},
];
});
const getClass = computed(() => {
return [
prefixCls,
{
[`${prefixCls}--mobile`]: unref(getIsMobile)
}
];
});
watch(
() => props.visible,
(visible: boolean) => {
visible &&
nextTick(() => {
unref(inputRef)?.focus();
});
},
);
watch(
() => props.visible,
(visible: boolean) => {
visible &&
nextTick(() => {
unref(inputRef)?.focus();
});
}
);
function handleClose() {
searchResult.value = [];
emit('close');
}
function handleClose() {
searchResult.value = [];
emit('close');
}
</script>
<style lang="less" scoped>
@prefix-cls: ~'@{namespace}-app-search-modal';
@footer-prefix-cls: ~'@{namespace}-app-search-footer';
.@{prefix-cls} {
position: fixed;
top: 0;
left: 0;
z-index: 800;
display: flex;
width: 100%;
height: 100%;
padding-top: 50px;
background-color: rgb(0 0 0 / 25%);
justify-content: center;
&--mobile {
padding: 0;
> div {
width: 100%;
}
.@{prefix-cls}-input {
width: calc(100% - 38px);
}
.@{prefix-cls}-cancel {
display: inline-block;
}
.@{prefix-cls}-content {
width: 100%;
height: 100%;
border-radius: 0;
}
.@{footer-prefix-cls} {
display: none;
}
.@{prefix-cls}-list {
height: calc(100% - 80px);
max-height: unset;
&__item {
&-enter {
opacity: 0% !important;
}
}
}
}
&-content {
position: relative;
width: 632px;
margin: 0 auto auto;
background-color: @component-background;
border-radius: 16px;
box-shadow: 0 25px 50px -12px rgb(0 0 0 / 25%);
flex-direction: column;
}
&-input__wrapper {
display: flex;
padding: 14px 14px 0;
justify-content: space-between;
align-items: center;
}
&-input {
width: 100%;
height: 48px;
font-size: 1.5em;
color: #1c1e21;
border-radius: 6px;
span[role='img'] {
color: #999;
}
}
&-cancel {
display: none;
font-size: 1em;
color: #666;
}
&-not-data {
display: flex;
width: 100%;
height: 100px;
font-size: 0.9;
color: rgb(150 159 175);
align-items: center;
justify-content: center;
}
&-list {
max-height: 472px;
padding: 0 14px;
padding-bottom: 20px;
margin: 0 auto;
margin-top: 14px;
overflow: auto;
&__item {
position: relative;
@prefix-cls: ~'@{namespace}-app-search-modal';
@footer-prefix-cls: ~'@{namespace}-app-search-footer';
.@{prefix-cls} {
position: fixed;
top: 0;
left: 0;
z-index: 800;
display: flex;
width: 100%;
height: 56px;
padding-bottom: 4px;
padding-left: 14px;
margin-top: 8px;
font-size: 14px;
color: @text-color-base;
cursor: pointer;
background-color: @component-background;
border-radius: 4px;
box-shadow: 0 1px 3px 0 #d4d9e1;
align-items: center;
height: 100%;
padding-top: 50px;
background-color: rgb(0 0 0 / 25%);
justify-content: center;
> div:first-child,
> div:last-child {
display: flex;
align-items: center;
&--mobile {
padding: 0;
> div {
width: 100%;
}
.@{prefix-cls}-input {
width: calc(100% - 38px);
}
.@{prefix-cls}-cancel {
display: inline-block;
}
.@{prefix-cls}-content {
width: 100%;
height: 100%;
border-radius: 0;
}
.@{footer-prefix-cls} {
display: none;
}
.@{prefix-cls}-list {
height: calc(100% - 80px);
max-height: unset;
&__item {
&-enter {
opacity: 0% !important;
}
}
}
}
&--active {
color: #fff;
background-color: @primary-color;
.@{prefix-cls}-list__item-enter {
opacity: 100%;
}
&-content {
position: relative;
width: 632px;
margin: 0 auto auto;
background-color: @component-background;
border-radius: 16px;
box-shadow: 0 25px 50px -12px rgb(0 0 0 / 25%);
flex-direction: column;
}
&-icon {
width: 30px;
&-input__wrapper {
display: flex;
padding: 14px 14px 0;
justify-content: space-between;
align-items: center;
}
&-text {
flex: 1;
&-input {
width: 100%;
height: 48px;
font-size: 1.5em;
color: #1c1e21;
border-radius: 6px;
span[role='img'] {
color: #999;
}
}
&-enter {
width: 30px;
opacity: 0%;
&-cancel {
display: none;
font-size: 1em;
color: #666;
}
&-not-data {
display: flex;
width: 100%;
height: 100px;
font-size: 0.9;
color: rgb(150 159 175);
align-items: center;
justify-content: center;
}
&-list {
max-height: 472px;
padding: 0 14px;
padding-bottom: 20px;
margin: 0 auto;
margin-top: 14px;
overflow: auto;
&__item {
position: relative;
display: flex;
width: 100%;
height: 56px;
padding-bottom: 4px;
padding-left: 14px;
margin-top: 8px;
font-size: 14px;
color: @text-color-base;
cursor: pointer;
background-color: @component-background;
border-radius: 4px;
box-shadow: 0 1px 3px 0 #d4d9e1;
align-items: center;
> div:first-child,
> div:last-child {
display: flex;
align-items: center;
}
&--active {
color: #fff;
background-color: @primary-color;
.@{prefix-cls}-list__item-enter {
opacity: 100%;
}
}
&-icon {
width: 30px;
}
&-text {
flex: 1;
}
&-enter {
width: 30px;
opacity: 0%;
}
}
}
}
}
}
</style>