feat: 菜单增加分类

feat: 字典增加icon
feat: 样式优化
feat: 启动端口调整
This commit is contained in:
qiufeng
2024-11-15 23:13:53 +08:00
parent 669f434a37
commit b588c4bbb1
28 changed files with 1099 additions and 977 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
# port
VITE_PORT = 5281
VITE_PORT = 5186
# title
VITE_GLOB_APP_TITLE = 摸鱼低代码平台
+1 -1
View File
@@ -27,4 +27,4 @@ VITE_GLOB_OAUTH2_URL = http://localhost:8888/oauth2/authorize
# VITE_GLOB_OAUTH2_URL = http://app.mfish.com.cn:11116/oauth2/authorize
# oauth2回调地址
VITE_GLOB_OAUTH2_REDIRECT_URI = http://localhost:5281/oauth2
VITE_GLOB_OAUTH2_REDIRECT_URI = http://localhost:5186/oauth2
+1 -1
View File
@@ -12,5 +12,5 @@ RUN npm run build
FROM nginx:stable-alpine as production-stage
COPY --from=build-stage /app/mfish-nocode-view /usr/share/nginx/html/mfish-nocode-view
COPY --from=build-stage /app/config/mfish-nocode-view.conf /etc/nginx/conf.d/mfish-nocode-view.conf
EXPOSE 5281
EXPOSE 5186
CMD ["nginx", "-g", "daemon off;"]
+1 -1
View File
@@ -99,7 +99,7 @@ pnpm run build
- 访问前台
http://localhost:5281
http://localhost:5186
## 功能模块
+1 -1
View File
@@ -3,5 +3,5 @@ services:
container_name: mfish-nocode-view
image: mfish-nocode-view
ports:
- 11119:5281
- 11119:5186
restart: always
+10 -10
View File
@@ -25,26 +25,26 @@
}
},
"dependencies": {
"eslint-config-turbo": "^2.2.3",
"eslint-config-turbo": "^2.3.0",
"eslint-plugin-command": "^0.2.6"
},
"devDependencies": {
"@eslint/js": "^9.13.0",
"@eslint/js": "^9.14.0",
"@types/eslint": "^9.6.1",
"@typescript-eslint/eslint-plugin": "^8.11.0",
"@typescript-eslint/parser": "^8.11.0",
"eslint": "^9.13.0",
"@typescript-eslint/eslint-plugin": "^8.14.0",
"@typescript-eslint/parser": "^8.14.0",
"eslint": "^9.14.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-eslint-comments": "^3.2.0",
"eslint-plugin-jsdoc": "^50.4.3",
"eslint-plugin-jsonc": "^2.16.0",
"eslint-plugin-jsdoc": "^50.5.0",
"eslint-plugin-jsonc": "^2.18.1",
"eslint-plugin-no-only-tests": "^3.3.0",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-regexp": "^2.6.0",
"eslint-plugin-regexp": "^2.7.0",
"eslint-plugin-unicorn": "^55.0.0",
"eslint-plugin-unused-imports": "^4.1.4",
"eslint-plugin-vue": "^9.30.0",
"globals": "^15.11.0",
"eslint-plugin-vue": "^9.31.0",
"globals": "^15.12.0",
"jsonc-eslint-parser": "^2.4.0",
"vue-eslint-parser": "^9.4.3"
}
+2 -2
View File
@@ -23,10 +23,10 @@
"dependencies": {
"@stylistic/stylelint-plugin": "^3.1.1",
"stylelint-config-recess-order": "^5.1.1",
"stylelint-scss": "^6.8.1"
"stylelint-scss": "^6.9.0"
},
"devDependencies": {
"postcss": "^8.4.47",
"postcss": "^8.4.49",
"postcss-html": "^1.7.0",
"postcss-scss": "^4.0.9",
"prettier": "^3.3.3",
+2 -2
View File
@@ -18,7 +18,7 @@
"web.json"
],
"dependencies": {
"@types/node": "^22.8.1",
"vite": "^5.4.10"
"@types/node": "^22.9.0",
"vite": "^5.4.11"
}
}
+5 -5
View File
@@ -26,20 +26,20 @@
},
"dependencies": {
"@ant-design/colors": "^7.1.0",
"vite": "^5.4.10"
"vite": "^5.4.11"
},
"devDependencies": {
"@types/fs-extra": "^11.0.4",
"@vitejs/plugin-vue": "^5.1.4",
"@vitejs/plugin-vue-jsx": "^4.0.1",
"ant-design-vue": "^4.2.5",
"@vitejs/plugin-vue": "^5.2.0",
"@vitejs/plugin-vue-jsx": "^4.1.0",
"ant-design-vue": "^4.2.6",
"dayjs": "^1.11.13",
"dotenv": "^16.4.5",
"fs-extra": "^11.2.0",
"less": "^4.2.0",
"picocolors": "^1.1.1",
"pkg-types": "^1.2.1",
"postcss": "^8.4.47",
"postcss": "^8.4.49",
"rollup-plugin-visualizer": "^5.12.0",
"unbuild": "^2.0.0",
"unocss": "^0.61.9",
+18 -18
View File
@@ -25,7 +25,6 @@
"lint:prettier": "prettier --write \"src/**/*.{js,json,tsx,css,less,scss,vue,html,md}\"",
"lint:stylelint": "stylelint --cache --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/",
"lint:lint-staged": "lint-staged",
"gen:icon": "esno ./build/generate/icon/index.ts",
"preinstall": "npx only-allow pnpm"
},
"dependencies": {
@@ -36,7 +35,8 @@
"@vueuse/core": "^10.11.1",
"@vueuse/shared": "^10.11.1",
"@zxcvbn-ts/core": "^3.0.4",
"ant-design-vue": "^4.2.5",
"animate.css": "^4.1.1",
"ant-design-vue": "^4.2.6",
"axios": "^1.7.7",
"bootstrap": "^5.3.3",
"codemirror": "5.65.16",
@@ -46,8 +46,8 @@
"echarts": "^5.5.1",
"lodash-es": "^4.17.21",
"nprogress": "^0.2.0",
"path-to-regexp": "^7.2.0",
"pinia": "^2.2.4",
"path-to-regexp": "^8.2.0",
"pinia": "^2.2.6",
"qrcode": "^1.5.4",
"qs": "^6.13.0",
"resize-observer-polyfill": "^1.5.1",
@@ -63,7 +63,7 @@
"vuedraggable": "^4.1.0"
},
"devDependencies": {
"@iconify/json": "^2.2.265",
"@iconify/json": "^2.2.272",
"@logicflow/core": "^1.2.28",
"@logicflow/extension": "^1.2.28",
"@mfish/eslint-config": "workspace:*",
@@ -80,25 +80,25 @@
"@types/intro.js": "^5.1.5",
"@types/jsdom": "^21.1.7",
"@types/lodash-es": "^4.17.12",
"@types/node": "^22.8.1",
"@types/node": "^22.9.0",
"@types/qrcode": "^1.5.5",
"@types/sm-crypto": "^0.3.4",
"@types/sortablejs": "^1.15.8",
"@typescript-eslint/eslint-plugin": "^8.11.0",
"@typescript-eslint/parser": "^8.11.0",
"@typescript-eslint/eslint-plugin": "^8.14.0",
"@typescript-eslint/parser": "^8.14.0",
"@vitejs/plugin-legacy": "^5.4.3",
"@vitejs/plugin-vue": "^5.1.4",
"@vitejs/plugin-vue-jsx": "^4.0.1",
"@vitejs/plugin-vue": "^5.2.0",
"@vitejs/plugin-vue-jsx": "^4.1.0",
"@vue/eslint-config-prettier": "^9.0.0",
"@vue/test-utils": "^2.4.6",
"@vue/tsconfig": "^0.5.1",
"autoprefixer": "^10.4.20",
"cross-env": "^7.0.3",
"dotenv": "^16.4.5",
"eslint": "^9.13.0",
"eslint": "^9.14.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-vue": "^9.30.0",
"eslint-plugin-vue": "^9.31.0",
"esno": "^4.8.0",
"fs-extra": "^11.2.0",
"inquirer": "^10.2.2",
@@ -107,20 +107,20 @@
"naive-ui": "^2.40.1",
"npm-run-all": "^4.1.5",
"picocolors": "^1.1.1",
"postcss": "^8.4.47",
"postcss": "^8.4.49",
"prettier": "^3.3.3",
"prettier-plugin-packagejson": "^2.5.3",
"rimraf": "^6.0.1",
"rollup": "^4.24.2",
"rollup": "^4.27.0",
"rollup-plugin-visualizer": "^5.12.0",
"stylelint": "^16.10.0",
"stylelint-config-standard": "^36.0.1",
"stylelint-order": "^6.0.4",
"terser": "^5.36.0",
"turbo": "^2.2.3",
"turbo": "^2.3.0",
"typescript": "^5.6.3",
"unbuild": "^2.0.0",
"vite": "^5.4.10",
"vite": "^5.4.11",
"vite-plugin-html": "^3.2.2",
"vite-plugin-imagemin": "^0.6.1",
"vite-plugin-mkcert": "^1.17.6",
@@ -128,7 +128,7 @@
"vite-plugin-svg-icons": "^2.0.1",
"vite-plugin-vue-setup-extend": "^0.4.0",
"vue-eslint-parser": "^9.4.3",
"vue-tsc": "^2.1.8"
"vue-tsc": "^2.1.10"
},
"resolutions": {
"bin-wrapper": "npm:bin-wrapper-china",
@@ -154,5 +154,5 @@
"node": ">=20",
"pnpm": ">=9"
},
"packageManager": "pnpm@9.12.2"
"packageManager": "pnpm@9.12.3"
}
+907 -855
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -12,6 +12,7 @@ export interface DictItem extends BaseEntity<string> {
dictValue: string;
valueType: number;
dictSort: number;
icon: string;
color: string;
status: number;
remark: string;
@@ -24,9 +24,9 @@
<template #item="{ index, element }">
<div @mouseover="tagMouseOver(index)" @mouseleave="tagMouseLeave" style="position: relative">
<div @drop="handleDropItem($event, index)">
<slot v-bind="{ index, element } || {}" name="tag" v-if="element.slot"></slot>
<slot v-bind="{ index, element }" name="tag" v-if="element.slot"></slot>
<ATag v-else class="pointer" :color="blockColor" @click="editBlock(index, element)">
<template #icon> <slot v-bind="{ index, element } || {}" name="tagIcon"></slot> </template>
<template #icon> <slot v-bind="{ index, element }" name="tagIcon"></slot> </template>
{{ element.name }}
</ATag>
</div>
+2 -2
View File
@@ -99,10 +99,10 @@
};
}
function handlePreview(file) {
function handlePreview(file: any) {
filePreviewRef.value?.show({ fileKey: file.response.fileKey, fileType: file.type });
}
function handleRemove(file) {
function handleRemove(file: any) {
emit("remove", toRaw(unref(file)), getFiles());
}
function getFiles() {
+17 -11
View File
@@ -1,12 +1,12 @@
<template>
<SvgIcon :size="size" :name="getSvgIcon" v-if="isSvgIcon" class="anticon" :class="[$attrs.class]" :spin="spin" />
<IconFont v-else-if="isIconFont" :icon="getIconfont" :size="size" :color="color" />
<IconFont v-else-if="isIconFont" :icon="getIconfont" :size="getWrapStyle.fontSize" :color="getWrapStyle.color" />
<span v-else class="app-iconify anticon" :class="[$attrs.class, spin && 'app-iconify-spin']" :style="getWrapStyle">
<Icon :icon="getIconRef" />
</span>
</template>
<script lang="ts" setup>
import { PropType, computed, ComputedRef } from "vue";
import { PropType, computed } from "vue";
import SvgIcon from "./SvgIcon.vue";
import { Icon } from "@iconify/vue";
import { isString } from "@/utils/Is";
@@ -19,7 +19,7 @@
// icon name
icon: propTypes.string,
// icon color
color: [String, Object] as PropType<string | ComputedRef<string>>,
color: { type: String },
// icon size
size: {
type: [String, Number] as PropType<string | number>,
@@ -43,16 +43,22 @@
if (isString(size)) {
fs = Number.parseInt(size, 10);
}
const newColor = { color: "inherit" };
if (color) {
newColor.color =
color === "primary"
? defaultSeed.colorPrimary
: color === "success"
? defaultSeed.colorSuccess
: color === "warning"
? defaultSeed.colorWarning
: color === "error"
? defaultSeed.colorError
: color;
}
return {
fontSize: `${fs}px`,
color:
color === "success"
? defaultSeed.colorSuccess
: color === "warning"
? defaultSeed.colorWarning
: color === "error"
? defaultSeed.colorError
: color,
...newColor,
display: "inline-flex"
};
});
+58 -43
View File
@@ -1,16 +1,18 @@
<template>
<AInput
disabled
<Input
:style="{ width }"
:placeholder="t('component.icon.placeholder')"
:class="prefixCls"
v-model:value="currentSelect"
@click="triggerPopover"
:allow-clear="props.allowClear"
:readonly="props.readonly"
>
<template #addonAfter>
<APopover placement="bottomLeft" trigger="click" v-model="visible" :overlay-class-name="`${prefixCls}-popover`">
<Popover placement="bottomLeft" trigger="click" v-model="visible" :overlay-class-name="`${prefixCls}-popover`">
<template #title>
<div class="flex justify-between">
<AInput :placeholder="t('component.icon.search')" @change="debounceHandleSearchChange" allow-clear />
<Input :placeholder="t('component.icon.search')" @change="debounceHandleSearchChange" allow-clear />
</div>
</template>
@@ -26,14 +28,13 @@
@click="handleClick(icon)"
:title="icon"
>
<!-- <Icon :icon="icon" :prefix="prefix" /> -->
<SvgIcon v-if="isSvgMode" :name="icon" />
<Icon :icon="icon" v-else />
</li>
</ul>
</ScrollContainer>
<div class="flex py-2 items-center justify-center" v-if="getTotal >= pageSize">
<APagination
<Pagination
show-less-items
size="small"
:page-size="pageSize"
@@ -43,19 +44,19 @@
</div>
</div>
<template v-else>
<div class="p-5">
<AEmpty />
</div>
<div class="p-5"><Empty /> </div>
</template>
</template>
<span class="cursor-pointer px-2 py-1 flex items-center" v-if="isSvgMode && currentSelect">
<SvgIcon :name="currentSelect" />
</span>
<Icon :icon="currentSelect || 'ion:apps-outline'" class="cursor-pointer px-2 py-1" v-else />
</APopover>
<div ref="trigger">
<span class="cursor-pointer px-2 py-1 flex items-center" v-if="isSvgMode && currentSelect">
<SvgIcon :name="currentSelect" />
</span>
<Icon :icon="currentSelect || 'ion:apps-outline'" class="cursor-pointer px-2 py-1" v-else />
</div>
</Popover>
</template>
</AInput>
</Input>
</template>
<script lang="ts" setup>
import { ref, watchEffect, watch, unref } from "vue";
@@ -65,42 +66,45 @@
import Icon from "./Icon.vue";
import SvgIcon from "./SvgIcon.vue";
import iconsData from "../data/icons.data";
import { propTypes } from "@/utils/PropTypes";
import { usePagination } from "@/hooks/web/UsePagination";
import { useDebounceFn } from "@vueuse/core";
import { useI18n } from "@/hooks/web/UseI18n";
import { useCopyToClipboard } from "@/hooks/web/UseCopyToClipboard";
import { useMessage } from "@/hooks/web/UseMessage";
import svgIcons from "virtual:svg-icons-names";
const props = defineProps({
value: propTypes.string,
width: propTypes.string.def("100%"),
pageSize: propTypes.number.def(140),
copy: propTypes.bool.def(false),
mode: propTypes.oneOf<("svg" | "iconify")[]>(["svg", "iconify"]).def("iconify")
import { useMessage } from "@/hooks/web/UseMessage";
interface Props {
value?: string;
width?: string;
pageSize?: number;
copy?: boolean;
mode?: "svg" | "iconify";
allowClear?: boolean;
readonly?: boolean;
}
// Don't inherit FormItem disabled、placeholder...
defineOptions({
inheritAttrs: false
});
const props = withDefaults(defineProps<Props>(), {
value: "",
width: "100%",
pageSize: 140,
copy: false,
mode: "iconify",
allowClear: true,
readonly: false
});
const emit = defineEmits(["change", "update:value"]);
// 没有使用别名引入,是因为WebStorm当前版本还不能正确识别,会报unused警告
const AInput = Input;
const APopover = Popover;
const APagination = Pagination;
const AEmpty = Empty;
function getIcons() {
const data = iconsData as any;
const prefix: string = data?.prefix ?? "";
let result: string[] = [];
if (prefix) {
result = (data?.icons ?? []).map((item) => `${prefix}:${item}`);
} else if (Array.isArray(iconsData)) {
result = iconsData as string[];
}
return result;
const prefix = iconsData.prefix;
return iconsData.icons.map((icon) => `${prefix}:${icon}`);
}
function getSvgIcons() {
return svgIcons.map((icon) => icon.replace("icon-", ""));
return svgIcons.map((icon: string) => icon.replace("icon-", ""));
}
const isSvgMode = props.mode === "svg";
@@ -109,6 +113,13 @@
const currentSelect = ref("");
const visible = ref(false);
const currentList = ref(icons);
const trigger = ref<HTMLDivElement>();
const triggerPopover = () => {
if (trigger.value) {
trigger.value.click();
}
};
const { t } = useI18n();
const { prefixCls } = useDesign("icon-picker");
@@ -127,10 +138,9 @@
() => currentSelect.value,
(v) => {
emit("update:value", v);
return emit("change", v);
emit("change", v);
}
);
function handlePageChange(page: number) {
setCurrentPage(page);
}
@@ -145,8 +155,9 @@
}
}
function handleSearchChange(e: ChangeEvent) {
const value = e.target.value;
function handleSearchChange(e: Event) {
const value = (e.target as HTMLInputElement).value;
if (!value) {
setCurrentPage(1);
currentList.value = icons;
@@ -163,6 +174,10 @@
padding: 0;
}
.ant-input {
cursor: pointer;
}
&-popover {
width: 300px;
+15 -2
View File
@@ -46,7 +46,8 @@
.modal-icon-info {
color: @button-primary-color !important;
}
.ant-layout-header{
.ant-layout-header {
height: @header-height !important;
padding-inline: 0 !important;
line-height: @header-height !important;
@@ -68,9 +69,21 @@
> div {
> div {
max-width: 100%;
> div{
> div {
width: 100%;
}
}
}
}
.ant-segmented-item-selected {
.ant-segmented-item-label {
color: @main-color;
font-weight: bold;
}
}
.ant-segmented-item {
margin: 0 2px 0 2px;
}
+5 -3
View File
@@ -107,11 +107,13 @@ html {
// ==============screen=========
// =================================
@screen-item-normal-color: #666;
@screen-item-normal-color-light: #999;
@screen-input-bg-color-dark: #141414;
@screen-item-normal-hover-color: lighten(@screen-item-normal-color, 10%);;
@screen-header-color-light: #f0f2f5;
@screen-header-color-light: #f0f1f3;
@screen-header-color-dark: #232324;
@screen-header-color-light-hover: lighten(@screen-header-color-light, 1%);
@screen-header-color-dark-hover: lighten(@screen-header-color-dark, 1%);
@screen-header-color-light-hover: #f8f8f9;
@screen-header-color-dark-hover: #292929;
// =================================
// ==============button=============
// =================================
+2 -2
View File
@@ -12,7 +12,7 @@ import { useI18n } from "@/hooks/web/UseI18n";
import { useLocaleStore } from "@/store/modules/I18n";
export function useTitle() {
const { title } = useGlobSetting();
const { shortName } = useGlobSetting();
const { currentRoute } = useRouter();
const pageTitle = usePageTitle();
const { t } = useI18n();
@@ -25,7 +25,7 @@ export function useTitle() {
return;
}
const tTitle = t(route?.meta?.title as string);
pageTitle.value = tTitle ? `${tTitle}-${title}` : title;
pageTitle.value = tTitle ? `${tTitle}-${shortName}` : shortName;
},
{ immediate: true }
);
+2 -2
View File
@@ -1,5 +1,5 @@
import type { App } from "vue";
import type { I18n, I18nOptions } from "vue-i18n";
import type { I18nOptions } from "vue-i18n";
import { createI18n } from "vue-i18n";
import { setHtmlPageLang, setLoadLocalePool } from "./Helper";
import { localeSetting } from "@/settings/I18nSetting";
@@ -39,7 +39,7 @@ async function createI18nOptions(): Promise<I18nOptions> {
// setup i18n instance with glob
async function setupI18n(app: App) {
const options = await createI18nOptions();
i18n = createI18n(options) as I18n;
i18n = createI18n(options) as ReturnType<typeof createI18n>;
app.use(i18n);
}
+2
View File
@@ -3,6 +3,8 @@ import "virtual:unocss-devtools";
import "@/design/index.less";
import "virtual:svg-icons-register";
import "ant-design-vue/dist/reset.css";
// 引入动画
import "animate.css/animate.min.css";
import App from "./App.vue";
import { createApp } from "vue";
import { router, setupRouter } from "./router";
+1
View File
@@ -16,6 +16,7 @@ export interface AppRouteRecordRaw extends Omit<RouteRecordRaw, "children"> {
children?: AppRouteRecordRaw[];
props?: Recordable;
fullPath?: string;
hideChildrenInMenu?: boolean;
}
export interface MenuTag {
+2 -2
View File
@@ -7,7 +7,7 @@ import { curLoginType, oauth2Config } from "@/settings/LoginSetting";
import type { Recordable } from "@mfish/types";
const ROOT_PATH = RootRoute().path;
const whitePathList: PageEnum[] = [PageEnum.BASE_LOGIN, PageEnum.OAUTH_LOGIN, PageEnum.ERROR_LOGIN];
const whitePathList: Set<PageEnum> = new Set([PageEnum.BASE_LOGIN, PageEnum.OAUTH_LOGIN, PageEnum.ERROR_LOGIN]);
export function createPermissionGuard(router: Router) {
const userStore = useUserStoreWithOut();
@@ -15,7 +15,7 @@ export function createPermissionGuard(router: Router) {
router.beforeEach(async (to, from, next) => {
const token = userStore.getToken;
// 白名单路径允许直接访问
if (whitePathList.includes(to.path as PageEnum)) {
if (whitePathList.has(to.path as PageEnum)) {
// 如果访问登陆页面且token存在,判断token是否有效直接进入redirect地址或首页地址
if (to.path === PageEnum.BASE_LOGIN && token) {
// 增加try catch方式请求异常造成无法返回首页
+1
View File
@@ -17,6 +17,7 @@ import {
import { DEFAULT_CACHE_TIME } from "@/settings/EncryptionSetting";
import { toRaw } from "vue";
import { pick, omit } from "lodash-es";
import { Nullable } from "@mfish/types";
interface BasicStore {
[TOKEN_KEY]: string | number | null | undefined;
+1
View File
@@ -2,6 +2,7 @@ import { cacheCipher } from "@/settings/EncryptionSetting";
import type { EncryptionParams } from "@/utils/Cipher";
import { AesEncryption } from "@/utils/Cipher";
import { isNullOrUnDef } from "@/utils/Is";
import { Nullable } from "@mfish/types";
export interface CreateStorageParams extends EncryptionParams {
prefixKey: string;
+4 -1
View File
@@ -6,7 +6,9 @@
<template>
<div :class="`${prefixCls}`">
<div class="header">
<div class="title">{{ `数据表:${curNode?.tableName?.toUpperCase()}` }}</div>
<div class="title">
{{ `数据表:${curNode?.tableName?.toUpperCase() ? curNode.tableName.toUpperCase() : "无"}` }}
</div>
<div><slot name="button" v-bind="{ data: curNode }"></slot></div>
</div>
<ATabs default-active-key="1" @change="changeTab">
@@ -90,6 +92,7 @@
}
async function buildTableData(page?: any) {
if (!condition.value.connectId) return;
await nextTick();
tableLoading(true);
let pageNum = 1;
+14
View File
@@ -2,6 +2,7 @@ import { BasicColumn } from "@/components/general/Table";
import { FormSchema } from "@/components/general/Table";
import { h } from "vue";
import { Tag } from "ant-design-vue";
import { Icon } from "@/components/general/Icon";
/**
* @description: 字典项
@@ -37,6 +38,14 @@ export const columns: BasicColumn[] = [
return color;
}
},
{
title: "图标",
dataIndex: "icon",
width: 50,
customRender: ({ record }) => {
return h(Icon, { icon: record.icon, color: record.color });
}
},
{
title: "值类型",
dataIndex: "valueType",
@@ -160,6 +169,11 @@ export const dictItemFormSchema: FormSchema[] = [
label: "字典颜色",
component: "Input"
},
{
field: "icon",
label: "图标",
component: "IconPicker"
},
{
field: "remark",
label: "备注",
+21 -10
View File
@@ -14,6 +14,15 @@ export const columns: BasicColumn[] = [
width: 200,
align: "left"
},
{
dataIndex: "menuType",
title: "菜单类型",
width: 80,
customRender: ({ value }) =>
h(Tag, value === 0 ? { color: "green" } : value === 1 ? { color: "blue" } : {}, () =>
value === 0 ? "目录" : value === 1 ? "菜单" : value === 2 ? "按钮" : "未知"
)
},
{
title: "菜单状态",
dataIndex: "isVisible",
@@ -227,8 +236,10 @@ export class MenuDesc {
{
field: "menuType",
label: "菜单类型",
render: (val) =>
h(Tag, { color: "green" }, () => (val === 0 ? "目录" : val === 1 ? "菜单" : val === 2 ? "按钮" : "未知"))
render: (value) =>
h(Tag, value === 0 ? { color: "green" } : value === 1 ? { color: "blue" } : {}, () =>
value === 0 ? "目录" : value === 1 ? "菜单" : value === 2 ? "按钮" : "未知"
)
},
{
field: "menuName",
@@ -257,7 +268,7 @@ export class MenuDesc {
field: "menuIcon",
label: "图标",
show: (values) => {
if (!values) return;
if (!values) return false;
return !isButton(values.menuType);
}
},
@@ -265,7 +276,7 @@ export class MenuDesc {
field: "routePath",
label: "路由地址",
show: (values) => {
if (!values) return;
if (!values) return false;
return !isButton(values.menuType);
}
},
@@ -273,7 +284,7 @@ export class MenuDesc {
field: "component",
label: "组件路径",
show: (values) => {
if (!values) return;
if (!values) return false;
return isMenu(values.menuType);
}
},
@@ -281,7 +292,7 @@ export class MenuDesc {
field: "permissions",
label: "权限标识",
show: (values) => {
if (!values) return;
if (!values) return false;
return isButton(values.menuType);
}
},
@@ -289,7 +300,7 @@ export class MenuDesc {
field: "isVisible",
label: "菜单状态",
show: (values) => {
if (!values) return;
if (!values) return false;
return !isButton(values.menuType);
},
render: (val) => YNTag_Name(val, "显示", "隐藏")
@@ -298,7 +309,7 @@ export class MenuDesc {
field: "activeMenu",
label: "激活菜单",
show: (values) => {
if (!values) return;
if (!values) return false;
return isMenu(values.menuType) && values.isVisible === 0;
}
},
@@ -306,7 +317,7 @@ export class MenuDesc {
field: "isExternal",
label: "新窗口打开",
show: (values) => {
if (!values) return;
if (!values) return false;
return isMenu(values.menuType);
},
render: (val) => YNTag(val)
@@ -315,7 +326,7 @@ export class MenuDesc {
field: "isKeepalive",
label: "是否缓存",
show: (values) => {
if (!values) return;
if (!values) return false;
return isMenu(values.menuType);
},
render: (val) => YNTag(val)