feat: 更新前端配置和组件逻辑

- 在tsconfig.json中排除特定示例文件夹
- 移除不再使用的路由别名清理逻辑,更新为手动修改提示
- 统一组件中对utils路径的引用,简化导入
- 优化多个组件的样式和布局,确保一致性
- 更新国际化文件,调整语言选项格式
This commit is contained in:
zhangtao
2026-05-06 23:53:13 +08:00
parent 62de57735e
commit a16473dc06
143 changed files with 3909 additions and 5969 deletions
@@ -120,7 +120,7 @@
<script setup lang="ts">
import ArtSvgIcon from "@/components/Core/base/art-svg-icon/index.vue";
import { resolveIconForArtSvgIcon } from "@/utils/menuIconRemix";
import { resolveIconForArtSvgIcon } from "@/utils/menuIcon/remix";
import { nextTick, onBeforeUnmount, onMounted, watch, ref, computed } from "vue";
import { useRouter } from "vue-router";
import { ElMessage } from "element-plus";
@@ -7,7 +7,7 @@
<script setup lang="ts">
import ArtSvgIcon from "@/components/Core/base/art-svg-icon/index.vue";
import { resolveIconForArtSvgIcon } from "@/utils/menuIconRemix";
import { resolveIconForArtSvgIcon } from "@/utils/menuIcon/remix";
const { isFullscreen, toggle } = useFullscreen();
</script>
@@ -10,7 +10,7 @@
<script setup lang="ts">
import ArtSvgIcon from "@/components/Core/base/art-svg-icon/index.vue";
import { resolveIconForArtSvgIcon } from "@/utils/menuIconRemix";
import { resolveIconForArtSvgIcon } from "@/utils/menuIcon/remix";
defineProps({
isActive: { type: Boolean, required: true },
@@ -86,7 +86,7 @@
import * as ElementPlusIconsVue from "@element-plus/icons-vue";
import ArtSvgIcon from "@/components/Core/base/art-svg-icon/index.vue";
import { isIconifyStoredIcon, resolveElementPlusIconComponent } from "@/utils/menuIcon";
import { resolveIconForArtSvgIcon } from "@/utils/menuIconRemix";
import { resolveIconForArtSvgIcon } from "@/utils/menuIcon/remix";
const props = defineProps({
modelValue: {
@@ -23,7 +23,7 @@
import ArtSvgIcon from "@/components/Core/base/art-svg-icon/index.vue";
import { useAppStore } from "@/store/modules/app.store";
import { LanguageEnum } from "@/enums/settings/locale.enum";
import { resolveIconForArtSvgIcon } from "@/utils/menuIconRemix";
import { resolveIconForArtSvgIcon } from "@/utils/menuIcon/remix";
defineProps({
size: {
@@ -36,7 +36,7 @@ import {
isElementPlusStoredIcon,
resolveElementPlusIconComponent,
} from "@/utils/menuIcon";
import { resolveIconForArtSvgIcon } from "@/utils/menuIconRemix";
import { resolveIconForArtSvgIcon } from "@/utils/menuIcon/remix";
defineOptions({ name: "MenuRouteIcon", inheritAttrs: false });
@@ -44,7 +44,7 @@ const props = defineProps<{
icon?: string;
color?: string;
/** 与 Vue `class` 一致(含条件 `false`) */
class?: import("vue").ClassValue;
class?: string | string[] | Record<string, boolean> | null | undefined | false;
style?: Record<string, unknown> | string;
}>();
@@ -130,7 +130,7 @@
import ArtSvgIcon from "@/components/Core/base/art-svg-icon/index.vue";
import MenuRouteIcon from "@/components/MenuRouteIcon/index.vue";
import { router } from "@/router";
import { resolveIconForArtSvgIcon } from "@/utils/menuIconRemix";
import { resolveIconForArtSvgIcon } from "@/utils/menuIcon/remix";
import { usePermissionStore } from "@/store";
import { isExternal } from "@/utils";
import { RouteRecordRaw, LocationQueryRaw } from "vue-router";
@@ -87,7 +87,7 @@
<script setup lang="ts">
import ArtSvgIcon from "@/components/Core/base/art-svg-icon/index.vue";
import NoticeAPI, { NoticeTable } from "@/api/module_system/notice";
import { resolveIconForArtSvgIcon } from "@/utils/menuIconRemix";
import { resolveIconForArtSvgIcon } from "@/utils/menuIcon/remix";
import { ArrowRight, Timer, User } from "@element-plus/icons-vue";
import { router } from "@/router";
import { useNoticeStore } from "@/store";
@@ -28,7 +28,7 @@
import ArtIconButton from "@/components/Core/widget/art-icon-button/index.vue";
import { ComponentSize } from "@/enums/settings/layout.enum";
import { useAppStore } from "@/store/modules/app.store";
import { resolveIconForArtSvgIcon } from "@/utils/menuIconRemix";
import { resolveIconForArtSvgIcon } from "@/utils/menuIcon/remix";
import { computed } from "vue";
const { t } = useI18n();
@@ -118,7 +118,7 @@ import {
ElTreeSelect,
type FormInstance,
} from "element-plus";
import { calculateResponsiveSpan, type ResponsiveBreakpoint } from "@/utils/form/responsive";
import { calculateResponsiveSpan, type ResponsiveBreakpoint } from "@/utils/form";
defineOptions({ name: "ArtForm" });
@@ -131,7 +131,7 @@ import {
ElTreeSelect,
type FormInstance,
} from "element-plus";
import { calculateResponsiveSpan, type ResponsiveBreakpoint } from "@/utils/form/responsive";
import { calculateResponsiveSpan, type ResponsiveBreakpoint } from "@/utils/form";
defineOptions({ name: "ArtSearchBar" });
@@ -22,9 +22,10 @@ import "@wangeditor/editor/dist/css/style.css";
import { onBeforeUnmount, onMounted, shallowRef, computed } from "vue";
import { Editor, Toolbar } from "@wangeditor/editor-for-vue";
import { useUserStore } from "@/store/modules/user.store";
import EmojiText from "@/utils/ui/emojo";
import { EmojiText } from "@/utils/ui";
import { IDomEditor, IToolbarConfig, IEditorConfig } from "@wangeditor/editor";
import request from "@/utils/http";
import type { AxiosResponse } from "axios";
defineOptions({ name: "ArtWangEditor" });
@@ -136,22 +137,26 @@ const editorConfig: Partial<IEditorConfig> = {
};
// 自定义上传
if (props.uploadConfig?.isCustomUpload && props.uploadConfig?.server && editorConfig.MENU_CONF) {
const uploadConfig = props.uploadConfig;
if (uploadConfig?.isCustomUpload && uploadConfig.server && editorConfig.MENU_CONF) {
const uploadServerUrl = uploadConfig.server;
editorConfig.MENU_CONF.uploadImage.customUpload = async (file: File, insertFn: InsertFnType) => {
try {
const formData = new FormData();
formData.append(mergedUploadConfig.value.fieldName, file);
const response = await request.post<{ url: string; alt: string; href: string }>({
url: props.uploadConfig?.server,
data: formData,
type UploadImagePayload = { url: string; alt?: string; href?: string };
const response = await request.post<
ApiResponse<UploadImagePayload>,
AxiosResponse<ApiResponse<UploadImagePayload>>
>(uploadServerUrl, formData, {
headers: {
"Content-Type": "multipart/form-data",
Authorization: userStore.accessToken,
},
});
const { url, alt, href } = response;
const { url, alt = "", href = "" } = response.data.data ?? ({} as any);
if (!url) {
throw new Error("上传失败,请检查服务端配置");
@@ -6,15 +6,20 @@
</div>
<div class="flex-c md:justify-end max-md:mt-3 max-sm:!hidden">
<!-- 搜索显示 -->
<div
<!-- 搜索区域显示/隐藏:默认展示搜索(未高亮);点按收起后高亮表示当前为隐藏状态 -->
<ElTooltip
v-if="showSearchBar != null"
class="button"
@click="search"
:class="showSearchBar ? 'active !bg-theme hover:!bg-theme/80' : ''"
placement="bottom"
:content="showSearchBar ? t('table.toolbar.hideSearch') : t('table.toolbar.showSearch')"
>
<ArtSvgIcon icon="ri:search-line" :class="showSearchBar ? 'text-white' : 'text-g-700'" />
</div>
<div
class="button"
@click="search"
:class="!showSearchBar ? 'active !bg-theme hover:!bg-theme/80' : ''"
>
<ArtSvgIcon icon="ri:search-line" :class="!showSearchBar ? 'text-white' : 'text-g-700'" />
</div>
</ElTooltip>
<!-- 刷新 -->
<div
@@ -109,14 +114,14 @@
<ArtSvgIcon icon="ri:settings-line" />
</div>
</template>
<div>
<ElCheckbox v-if="showZebra" v-model="isZebra" :value="true">
<div class="flex min-w-[200px] flex-col gap-2">
<ElCheckbox v-model="isZebra" :value="true">
{{ t("table.zebra") }}
</ElCheckbox>
<ElCheckbox v-if="showBorder" v-model="isBorder" :value="true">
<ElCheckbox v-model="isBorder" :value="true">
{{ t("table.border") }}
</ElCheckbox>
<ElCheckbox v-if="showHeaderBackground" v-model="isHeaderBackground" :value="true">
<ElCheckbox v-model="isHeaderBackground" :value="true">
{{ t("table.headerBackground") }}
</ElCheckbox>
</div>
@@ -141,12 +146,6 @@ defineOptions({ name: "ArtTableHeader" });
const { t } = useI18n();
interface Props {
/** 斑马纹 */
showZebra?: boolean;
/** 边框 */
showBorder?: boolean;
/** 表头背景 */
showHeaderBackground?: boolean;
/** 全屏 class */
fullClass?: string;
/** 组件布局,子组件名用逗号分隔 */
@@ -158,9 +157,6 @@ interface Props {
}
const props = withDefaults(defineProps<Props>(), {
showZebra: true,
showBorder: true,
showHeaderBackground: true,
fullClass: "art-page-view",
layout: "search,refresh,size,fullscreen,columns,settings",
showSearchBar: undefined,
@@ -32,7 +32,22 @@
</template>
<!-- 整段单行:插槽内兄弟节点之间的空白文本会让 EP 误判单元格内容 -->
<!-- eslint-disable-next-line vue/max-attributes-per-line, prettier/prettier -->
<template #default="slotScope"><slot v-if="col.useSlot && col.prop && shouldRenderSlotScope(slotScope)" :name="col.slotName || col.prop" v-bind="{ ...slotScope, prop: col.prop, value: col.prop ? slotScope.row[col.prop] : undefined }" /><TableFormatterOutlet v-else-if="col.formatter && !col.useSlot && shouldRenderSlotScope(slotScope)" :column="col" :record="slotScope.row" /></template>
<template #default="slotScope">
<slot
v-if="col.useSlot && col.prop && shouldRenderSlotScope(slotScope)"
:name="col.slotName || col.prop"
v-bind="{
...slotScope,
prop: col.prop,
value: col.prop ? slotScope.row[col.prop] : undefined,
}"
/>
<TableFormatterOutlet
v-else-if="col.formatter && !col.useSlot && shouldRenderSlotScope(slotScope)"
:column="col"
:record="slotScope.row"
/>
</template>
</ElTableColumn>
</template>
@@ -159,6 +174,13 @@ const props = withDefaults(defineProps<ArtTableProps>(), {
const instance = getCurrentInstance();
const attrs = useAttrs();
/** 仅当调用方显式传入对应 prop 时视为「固定」,否则交由表格 store */
const hasExplicitTableProp = (propName: string): boolean => {
const rawProps = (instance?.vnode.props || {}) as Record<string, unknown>;
const kebabName = propName.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
return propName in rawProps || kebabName in rawProps;
};
const LAYOUT = {
MOBILE: "prev, pager, next, sizes, jumper, total",
IPAD: "prev, pager, next, jumper, total",
@@ -268,13 +290,6 @@ const headerCellStyle = computed(() => ({
...(props.headerCellStyle || {}), // 合并用户传入的样式
}));
// 只有显式传入时才覆盖 ElTable 的原生默认值,避免继承的 Boolean props 把官方默认值冲掉。
const hasExplicitTableProp = (propName: string): boolean => {
const rawProps = (instance?.vnode.props || {}) as Record<string, unknown>;
const kebabName = propName.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
return propName in rawProps || kebabName in rawProps;
};
const mergedTableProps = computed(() => ({
...attrs,
...props,
@@ -120,7 +120,7 @@ import { useI18n } from "vue-i18n";
import { useSettingsStore } from "@/store/modules/setting.store";
import { useUserStore } from "@/store/modules/user.store";
import { useHeaderBar } from "@/hooks/core/useHeaderBar";
import { themeAnimation } from "@/utils/ui/animation";
import { themeAnimation } from "@/utils/ui";
import { languageOptions } from "@/locales";
import { LanguageEnum } from "@/enums/appEnum";
import AppConfig from "@/config";
@@ -81,7 +81,7 @@
import AppConfig from "@/config";
import loginIcon from "@imgs/svg/login_icon.svg";
import { useConfigStore } from "@/store/modules/config.store";
import { themeAnimation } from "@/utils/ui/animation";
import { themeAnimation } from "@/utils/ui";
defineProps<{
hideContent?: boolean;