Merge remote-tracking branch 'origin/develop' into develop
This commit is contained in:
2
web/.env
2
web/.env
@@ -1,6 +1,6 @@
|
||||
# port 端口号
|
||||
VITE_PORT = 8080
|
||||
|
||||
VITE_API_URL = 'http://dvadmin3api.django.icu:8001'
|
||||
# open 运行 npm run dev 时自动打开浏览器
|
||||
VITE_OPEN = false
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="layout-logo" v-if="setShowLogo" @click="onThemeConfigChange">
|
||||
<img :src="logoMini" class="layout-logo-medium-img" />
|
||||
<span style="font-size: x-large">{{ themeConfig.globalTitle }}</span>
|
||||
<span style="font-size: x-large">{{ getSystemConfig['login.site_title'] || themeConfig.globalTitle }}</span>
|
||||
</div>
|
||||
<div class="layout-logo-size" v-else @click="onThemeConfigChange">
|
||||
<img :src="logoMini" class="layout-logo-size-img" />
|
||||
@@ -13,6 +13,7 @@ import { computed } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeConfig } from '/@/stores/themeConfig';
|
||||
import logoMini from '/@/assets/logo-mini.svg';
|
||||
import {SystemConfigStore} from "/@/stores/systemConfig";
|
||||
|
||||
// 定义变量内容
|
||||
const storesThemeConfig = useThemeConfig();
|
||||
@@ -28,6 +29,13 @@ const onThemeConfigChange = () => {
|
||||
if (themeConfig.value.layout === 'transverse') return false;
|
||||
themeConfig.value.isCollapse = !themeConfig.value.isCollapse;
|
||||
};
|
||||
|
||||
const systemConfigStore = SystemConfigStore()
|
||||
const {systemConfig} = storeToRefs(systemConfigStore)
|
||||
const getSystemConfig = computed(()=>{
|
||||
return systemConfig.value
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -36,7 +36,7 @@ import { reactive, computed, onMounted } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeConfig } from '/@/stores/themeConfig';
|
||||
import { Local } from '/@/utils/storage';
|
||||
import { Local,Session } from '/@/utils/storage';
|
||||
|
||||
// 定义变量内容
|
||||
const { t } = useI18n();
|
||||
@@ -57,6 +57,7 @@ const getThemeConfig = computed(() => {
|
||||
// 残忍拒绝
|
||||
const onCancel = () => {
|
||||
state.isUpgrade = false;
|
||||
Session.set('isUpgrade', false)
|
||||
};
|
||||
// 马上更新
|
||||
const onUpgrade = () => {
|
||||
@@ -66,13 +67,17 @@ const onUpgrade = () => {
|
||||
Local.clear();
|
||||
window.location.reload();
|
||||
Local.set('version', state.version);
|
||||
Session.set('isUpgrade', false)
|
||||
}, 2000);
|
||||
};
|
||||
// 延迟显示,防止刷新时界面显示太快
|
||||
const delayShow = () => {
|
||||
setTimeout(() => {
|
||||
state.isUpgrade = true;
|
||||
}, 2000);
|
||||
const isUpgrade = Session.get('isUpgrade')===false?Session.get('isUpgrade'):true
|
||||
if(isUpgrade){
|
||||
setTimeout(() => {
|
||||
state.isUpgrade = true;
|
||||
}, 2000);
|
||||
}
|
||||
};
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
|
||||
@@ -12,14 +12,13 @@ import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
|
||||
import { useMenuApi } from '/@/api/menu/index';
|
||||
import { handleMenu } from '../utils/menu';
|
||||
import { BtnPermissionStore } from '/@/plugin/permission/store.permission';
|
||||
import {SystemConfigStore} from "/@/stores/systemConfig";
|
||||
|
||||
const menuApi = useMenuApi();
|
||||
|
||||
const layouModules: any = import.meta.glob('../layout/routerView/*.{vue,tsx}');
|
||||
const viewsModules: any = import.meta.glob('../views/**/*.{vue,tsx}');
|
||||
|
||||
// 后端控制路由
|
||||
|
||||
/**
|
||||
* 获取目录下的 .vue、.tsx 全部文件
|
||||
* @method import.meta.glob
|
||||
@@ -45,9 +44,12 @@ export async function initBackEndControlRoutes() {
|
||||
await useUserInfo().setUserInfos();
|
||||
// 获取路由菜单数据
|
||||
const res = await getBackEndControlRoutes();
|
||||
|
||||
// 无登录权限时,添加判断
|
||||
// https://gitee.com/lyt-top/vue-next-admin/issues/I64HVO
|
||||
if (res.data.length <= 0) return Promise.resolve(true);
|
||||
// 处理路由(component),替换 dynamicRoutes(/@/router/route)第一个顶级 children 的路由
|
||||
dynamicRoutes[0].children = await backEndComponent(handleMenu(res.data));
|
||||
const {frameIn,frameOut} = handleMenu(res.data)
|
||||
dynamicRoutes[0].children = await backEndComponent(frameIn);
|
||||
// 添加动态路由
|
||||
await setAddRoute();
|
||||
// 设置路由到 vuex routesList 中(已处理成多级嵌套路由)及缓存多级嵌套数组处理后的一维数组
|
||||
@@ -80,7 +82,10 @@ export function setCacheTagsViewRoutes() {
|
||||
* @returns 返回替换后的路由数组
|
||||
*/
|
||||
export function setFilterRouteEnd() {
|
||||
console.log(dynamicRoutes)
|
||||
let filterRouteEnd: any = formatTwoStageRoutes(formatFlatteningRoutes(dynamicRoutes));
|
||||
// notFoundAndNoPower 防止 404、401 不在 layout 布局中,不设置的话,404、401 界面将全屏显示
|
||||
// 关联问题 No match found for location with path 'xxx'
|
||||
filterRouteEnd[0].children = [...filterRouteEnd[0].children, ...notFoundAndNoPower];
|
||||
return filterRouteEnd;
|
||||
}
|
||||
@@ -105,6 +110,8 @@ export async function setAddRoute() {
|
||||
export function getBackEndControlRoutes() {
|
||||
//获取所有的按钮权限
|
||||
BtnPermissionStore().getBtnPermissionStore();
|
||||
// 获取系统配置
|
||||
SystemConfigStore().getSystemConfigs()
|
||||
return menuApi.getSystemMenu();
|
||||
}
|
||||
|
||||
@@ -126,6 +133,37 @@ export function backEndComponent(routes: any) {
|
||||
if (!routes) return;
|
||||
return routes.map((item: any) => {
|
||||
if (item.component) item.component = dynamicImport(dynamicViewsModules, item.component as string);
|
||||
if(item.is_catalog){
|
||||
// 对目录的处理
|
||||
item.component = dynamicImport(dynamicViewsModules, 'layout/routerView/parent')
|
||||
}
|
||||
if(item.is_link){
|
||||
// 对外链接的处理
|
||||
item.meta.isIframe = !item.is_iframe
|
||||
if(item.is_iframe){
|
||||
item.component = dynamicImport(dynamicViewsModules, 'layout/routerView/link')
|
||||
}else {
|
||||
item.component = dynamicImport(dynamicViewsModules, 'layout/routerView/iframes')
|
||||
}
|
||||
}else{
|
||||
console.log(item.is_iframe,item.web_path)
|
||||
console.log(router.getRoutes())
|
||||
if(item.is_iframe){
|
||||
const iframeRoute:RouteRecordRaw = {
|
||||
...item
|
||||
}
|
||||
console.log(iframeRoute)
|
||||
router.addRoute(iframeRoute)
|
||||
item.meta.isLink = item.path
|
||||
item.path = `${item.path}Link`
|
||||
item.name = `${item.name}Link`
|
||||
item.meta.isIframe = !item.is_iframe
|
||||
item.meta.isKeepAlive = false
|
||||
item.meta.isIframeOpen = true
|
||||
item.component = dynamicImport(dynamicViewsModules, 'layout/routerView/link.vue')
|
||||
|
||||
}
|
||||
}
|
||||
item.children && backEndComponent(item.children);
|
||||
return item;
|
||||
});
|
||||
|
||||
@@ -7,7 +7,7 @@ import {useKeepALiveNames} from '/@/stores/keepAliveNames';
|
||||
import {useRoutesList} from '/@/stores/routesList';
|
||||
import {useThemeConfig} from '/@/stores/themeConfig';
|
||||
import {Session} from '/@/utils/storage';
|
||||
import {staticRoutes} from '/@/router/route';
|
||||
import {notFoundAndNoPower,staticRoutes} from '/@/router/route';
|
||||
import {initFrontEndControlRoutes} from '/@/router/frontEnd';
|
||||
import {initBackEndControlRoutes} from '/@/router/backEnd';
|
||||
|
||||
@@ -32,7 +32,13 @@ const {isRequestRoutes} = themeConfig.value;
|
||||
*/
|
||||
export const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes: staticRoutes,
|
||||
/**
|
||||
* 说明:
|
||||
* 1、notFoundAndNoPower 默认添加 404、401 界面,防止一直提示 No match found for location with path 'xxx'
|
||||
* 2、backEnd.ts(后端控制路由)、frontEnd.ts(前端控制路由) 中也需要加 notFoundAndNoPower 404、401 界面。
|
||||
* 防止 404、401 不在 layout 布局中,不设置的话,404、401 界面将全屏显示
|
||||
*/
|
||||
routes: [...notFoundAndNoPower, ...staticRoutes]
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -63,14 +69,7 @@ export function formatTwoStageRoutes(arr: any) {
|
||||
const cacheList: Array<string> = [];
|
||||
arr.forEach((v: any) => {
|
||||
if (v.path === '/') {
|
||||
newArr.push({
|
||||
component: v.component,
|
||||
name: v.name,
|
||||
path: v.path,
|
||||
redirect: v.redirect,
|
||||
meta: v.meta,
|
||||
children: []
|
||||
});
|
||||
newArr.push({component: v.component,name: v.name,path: v.path,redirect: v.redirect,meta: v.meta,children: []});
|
||||
} else {
|
||||
// 判断是否是动态路由(xx/:id/:name),用于 tagsView 等中使用
|
||||
// 修复:https://gitee.com/lyt-top/vue-next-admin/issues/I3YX6G
|
||||
@@ -114,13 +113,13 @@ router.beforeEach(async (to, from, next) => {
|
||||
if (isRequestRoutes) {
|
||||
// 后端控制路由:路由数据初始化,防止刷新时丢失
|
||||
await initBackEndControlRoutes();
|
||||
// 动态添加路由:防止非首页刷新时跳转回首页的问题
|
||||
// 确保 addRoute() 时动态添加的路由已经被完全加载上去
|
||||
next({...to, replace: true});
|
||||
// 解决刷新时,一直跳 404 页面问题,关联问题 No match found for location with path 'xxx'
|
||||
// to.query 防止页面刷新时,普通路由带参数时,参数丢失。动态路由(xxx/:id/:name")isDynamic 无需处理
|
||||
next({ path: to.path, query: to.query });
|
||||
} else {
|
||||
// https://gitee.com/lyt-top/vue-next-admin/issues/I5F1HP
|
||||
await initFrontEndControlRoutes();
|
||||
next({...to, replace: true});
|
||||
next({ path: to.path, query: to.query });
|
||||
}
|
||||
} else {
|
||||
next();
|
||||
|
||||
@@ -88,4 +88,20 @@ export const staticRoutes: Array<RouteRecordRaw> = [
|
||||
title: '登录',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/operationLog',
|
||||
name: 'operationLog',
|
||||
component: () => import('/@/views/system/personal/index.vue'),
|
||||
meta: {
|
||||
title: 'message.router.personal'
|
||||
},
|
||||
},
|
||||
// {
|
||||
// path: '/demo',
|
||||
// name: 'demo',
|
||||
// component: () => import('/@/views/system/demo/index.vue'),
|
||||
// meta: {
|
||||
// title: 'message.router.personal'
|
||||
// },
|
||||
// }
|
||||
];
|
||||
|
||||
@@ -11,6 +11,7 @@ import { request } from '/@/utils/service';
|
||||
import { FsExtendsEditor,FsExtendsUploader } from '@fast-crud/fast-extends';
|
||||
import '@fast-crud/fast-extends/dist/style.css';
|
||||
import { successMessage, successNotification } from '/@/utils/message';
|
||||
import XEUtils from "xe-utils";
|
||||
export default {
|
||||
async install(app: any, options: any) {
|
||||
// 先安装ui
|
||||
@@ -20,8 +21,14 @@ export default {
|
||||
//i18n, //i18n配置,可选,默认使用中文,具体用法请看demo里的 src/i18n/index.js 文件
|
||||
// 此处配置公共的dictRequest(字典请求)
|
||||
async dictRequest({ dict }: any) {
|
||||
const {isTree} = dict
|
||||
console.log(222222,isTree)
|
||||
//根据dict的url,异步返回一个字典数组
|
||||
return await request({ url: dict.url, params: dict.params || {} }).then((res:any)=>{
|
||||
console.log(XEUtils.toArrayTree(res.data,{parentKey:'parent'}))
|
||||
if(isTree){
|
||||
return XEUtils.toArrayTree(res.data,{parentKey:'parent'})
|
||||
}
|
||||
return res.data
|
||||
});
|
||||
},
|
||||
@@ -104,7 +111,8 @@ export default {
|
||||
// 上传完成后的结果处理, 此处应返回格式为{url:xxx,key:xxx}
|
||||
return {
|
||||
url: getBaseURL() + ret.data.url,
|
||||
key: ret.data.id
|
||||
key: ret.data.id,
|
||||
...ret.data
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,5 +22,8 @@ export const useRoutesList = defineStore('routesList', {
|
||||
async setColumnsNavHover(bool: Boolean) {
|
||||
this.isColumnsNavHover = bool;
|
||||
},
|
||||
async addRoutesList(data: Array<string>) {
|
||||
this.routesList.push(data);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
130
web/src/utils/commonCrud.ts
Normal file
130
web/src/utils/commonCrud.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
import { dict } from "@fast-crud/fast-crud";
|
||||
export const commonCrudConfig = (options = {
|
||||
create_datetime: {
|
||||
form: false,
|
||||
table: false,
|
||||
search: false
|
||||
},
|
||||
update_datetime: {
|
||||
form: false,
|
||||
table: false,
|
||||
search: false
|
||||
},
|
||||
creator_name: {
|
||||
form: false,
|
||||
table: false,
|
||||
search: false
|
||||
},
|
||||
modifier_name: {
|
||||
form: false,
|
||||
table: false,
|
||||
search: false
|
||||
},
|
||||
dept_belong_id: {
|
||||
form: false,
|
||||
table: false,
|
||||
search: false
|
||||
},
|
||||
description: {
|
||||
form: false,
|
||||
table: false,
|
||||
search: false
|
||||
},
|
||||
}) => {
|
||||
return {
|
||||
description: {
|
||||
title: '备注',
|
||||
search: {
|
||||
show: options.description?.search || false
|
||||
},
|
||||
type: 'textarea',
|
||||
column: {
|
||||
show: options.description?.table || false,
|
||||
},
|
||||
form: {
|
||||
component: {
|
||||
show: options.description?.form || false,
|
||||
placeholder: '请输入内容',
|
||||
showWordLimit: true,
|
||||
maxlength: '200',
|
||||
}
|
||||
}
|
||||
},
|
||||
modifier_name: {
|
||||
title: '修改人',
|
||||
search: {
|
||||
show: options.modifier_name?.search || false
|
||||
},
|
||||
column: {
|
||||
width: 100,
|
||||
show: options.modifier_name?.table || false,
|
||||
}
|
||||
},
|
||||
update_datetime: {
|
||||
title: '更新时间',
|
||||
type: 'datetime',
|
||||
search: {
|
||||
show: options.update_datetime?.search || false
|
||||
},
|
||||
column: {
|
||||
width: 160,
|
||||
show: options.update_datetime?.table || false,
|
||||
}
|
||||
},
|
||||
creator_name: {
|
||||
title: '创建人',
|
||||
search: {
|
||||
show: options.creator_name?.search || false
|
||||
},
|
||||
column: {
|
||||
width: 100,
|
||||
show: options.creator_name?.table || false,
|
||||
}
|
||||
},
|
||||
create_datetime: {
|
||||
title: '创建时间',
|
||||
type: 'datetime',
|
||||
search: {
|
||||
show: options.create_datetime?.search || false
|
||||
},
|
||||
column: {
|
||||
width: 160,
|
||||
show: options.create_datetime?.table || false,
|
||||
}
|
||||
},
|
||||
dept_belong_id: {
|
||||
title: '所属部门',
|
||||
type: 'dict-tree',
|
||||
search: {
|
||||
show: false
|
||||
},
|
||||
dict: dict({
|
||||
url: '/api/system/dept/all_dept/',
|
||||
isTree: true,
|
||||
value: 'id',
|
||||
label: 'name',
|
||||
children: 'children' // 数据字典中children字段的属性名
|
||||
}),
|
||||
column: {
|
||||
width: 150,
|
||||
show: options.dept_belong_id?.table || false,
|
||||
},
|
||||
form: {
|
||||
component: {
|
||||
show: options.dept_belong_id?.form || false,
|
||||
multiple: false,
|
||||
clearable: true,
|
||||
props: {
|
||||
props: {
|
||||
// 为什么这里要写两层props
|
||||
// 因为props属性名与fs的动态渲染的props命名冲突,所以要多写一层
|
||||
label: "name",
|
||||
value: "id",
|
||||
}
|
||||
}
|
||||
},
|
||||
helper: "默认不填则为当前创建用户的部门ID"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import XEUtils from "xe-utils"
|
||||
import {dynamicRoutes, staticRoutes} from "/@/router/route";
|
||||
|
||||
/**
|
||||
* @description: 处理后端菜单数据格式
|
||||
@@ -10,27 +11,56 @@ export const handleMenu = (menuData: Array<any>) => {
|
||||
const handleMeta = (item: any) => {
|
||||
item.meta = {
|
||||
title: item.title,
|
||||
isLink: item.is_link,
|
||||
isLink: item.link_url,
|
||||
isHide: !item.visible,
|
||||
isKeepAlive: item.cache,
|
||||
isAffix: false,
|
||||
isIframe: false,
|
||||
isAffix: item.is_affix,
|
||||
isIframe: item.is_iframe,
|
||||
roles: ['admin'],
|
||||
icon: item.icon
|
||||
}
|
||||
item.name = item.component_name
|
||||
item.path = item.web_path
|
||||
return item
|
||||
}
|
||||
menuData.forEach((val) => {
|
||||
handleMeta(val)
|
||||
val.path = val.web_path
|
||||
})
|
||||
|
||||
const data = XEUtils.toArrayTree(menuData, {
|
||||
// 处理框架外的路由
|
||||
const handleFrame = (item: any) => {
|
||||
if (item.is_iframe) {
|
||||
item.meta = {
|
||||
title: item.title,
|
||||
isLink: item.link_url,
|
||||
isHide: !item.visible,
|
||||
isKeepAlive: item.cache,
|
||||
isAffix: item.is_affix,
|
||||
isIframe: item.is_iframe,
|
||||
roles: ['admin'],
|
||||
icon: item.icon
|
||||
}
|
||||
item.name = item.component_name
|
||||
item.path = item.web_path
|
||||
}
|
||||
return item
|
||||
}
|
||||
|
||||
// 框架内路由
|
||||
const defaultRoutes:Array<any> = []
|
||||
// 框架外路由
|
||||
const iframeRoutes:Array<any> = []
|
||||
|
||||
menuData.forEach((val) => {
|
||||
// if (val.is_iframe) {
|
||||
// // iframeRoutes.push(handleFrame(val))
|
||||
// } else {
|
||||
// defaultRoutes.push(handleMeta(val))
|
||||
// }
|
||||
defaultRoutes.push(handleMeta(val))
|
||||
})
|
||||
const data = XEUtils.toArrayTree(defaultRoutes, {
|
||||
parentKey: 'parent',
|
||||
strict: true,
|
||||
})
|
||||
const menu = [
|
||||
const dynamicRoutes = [
|
||||
{
|
||||
path: '/home', name: 'home', component: '/system/home/index', meta: {
|
||||
title: 'message.router.home',
|
||||
@@ -45,5 +75,5 @@ export const handleMenu = (menuData: Array<any>) => {
|
||||
},
|
||||
...data
|
||||
]
|
||||
return menu
|
||||
return {frameIn:dynamicRoutes,frameOut:iframeRoutes}
|
||||
}
|
||||
|
||||
@@ -96,12 +96,13 @@ function createService() {
|
||||
return dataAxios;
|
||||
case 4000:
|
||||
errorCreate(`${dataAxios.msg}: ${response.config.url}`);
|
||||
return dataAxios;
|
||||
break;
|
||||
default:
|
||||
// 不是正确的 code
|
||||
errorCreate(`${dataAxios.msg}: ${response.config.url}`);
|
||||
return dataAxios;
|
||||
break;
|
||||
}
|
||||
return Promise.reject(dataAxios);
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
const cssCdnUrlList: Array<string> = [
|
||||
'//at.alicdn.com/t/font_2298093_y6u00apwst.css',
|
||||
'//at.alicdn.com/t/c/font_3882322_9ah7y8m9175.css', //dvadmin3项目用icon
|
||||
'//netdna.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css'
|
||||
];
|
||||
// 第三方 js url
|
||||
const jsCdnUrlList: Array<string> = [];
|
||||
|
||||
13
web/src/views/system/demo/index.vue
Normal file
13
web/src/views/system/demo/index.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
测试框架外显示
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="demo">
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -126,6 +126,7 @@ export default defineComponent({
|
||||
});
|
||||
};
|
||||
const refreshCaptcha = async () => {
|
||||
state.ruleForm.captcha=''
|
||||
loginApi.getCaptcha().then((ret: any) => {
|
||||
state.ruleForm.captchaImgBase = ret.data.image_base;
|
||||
state.ruleForm.captchaKey = ret.data.key;
|
||||
@@ -150,11 +151,11 @@ export default defineComponent({
|
||||
// 执行完 initBackEndControlRoutes,再执行 signInSuccess
|
||||
loginSuccess();
|
||||
}
|
||||
} else if (res.code === 4000) {
|
||||
// 登录错误之后,刷新验证码
|
||||
refreshCaptcha();
|
||||
}
|
||||
});
|
||||
}).catch((err: any) => {
|
||||
// 登录错误之后,刷新验证码
|
||||
refreshCaptcha();
|
||||
});
|
||||
} else {
|
||||
errorMessage("请填写登录信息")
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<span class="login-right-warp-one"></span>
|
||||
<span class="login-right-warp-two"></span>
|
||||
<div class="login-right-warp-mian">
|
||||
<div class="login-right-warp-main-title">{{ getThemeConfig.globalTitle }} 欢迎您!</div>
|
||||
<div class="login-right-warp-main-title">{{ getSystemConfig['login.site_title'] || getThemeConfig.globalTitle }} 欢迎您!</div>
|
||||
<div class="login-right-warp-main-form">
|
||||
<div v-if="!state.isScan">
|
||||
<el-tabs v-model="state.tabsActiveName">
|
||||
|
||||
@@ -103,6 +103,9 @@ export const createCrudOptions = function ({ crudExpose, props,modelDialog,selec
|
||||
label:'title',
|
||||
value:'key'
|
||||
}),
|
||||
column:{
|
||||
sortable: true,
|
||||
},
|
||||
form: {
|
||||
rules: [
|
||||
// 表单校验规则
|
||||
@@ -113,6 +116,13 @@ export const createCrudOptions = function ({ crudExpose, props,modelDialog,selec
|
||||
],
|
||||
component: {
|
||||
span: 12,
|
||||
showSearch: true,
|
||||
filterable: true,
|
||||
//默认的filterOption仅支持value的过滤,label并不会加入查询
|
||||
//所以需要自定义filterOption
|
||||
filterOption(inputValue, option) {
|
||||
return option.label.indexOf(inputValue) >= 0 || option.value.indexOf(inputValue) >= 0;
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -143,6 +153,9 @@ export const createCrudOptions = function ({ crudExpose, props,modelDialog,selec
|
||||
search: {
|
||||
show: true,
|
||||
},
|
||||
column:{
|
||||
sortable: true,
|
||||
},
|
||||
form: {
|
||||
rules: [
|
||||
// 表单校验规则
|
||||
@@ -157,83 +170,6 @@ export const createCrudOptions = function ({ crudExpose, props,modelDialog,selec
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// is_create: {
|
||||
// title: '创建时显示',
|
||||
// sortable: 'custom',
|
||||
// search: {
|
||||
// disabled: true,
|
||||
// },
|
||||
// type: 'dict-switch',
|
||||
// dict: dict({
|
||||
// data: [
|
||||
// { value: true, label: '启用' },
|
||||
// { value: false, label: '禁用' },
|
||||
// ],
|
||||
// }),
|
||||
// form: {
|
||||
// value: true,
|
||||
// },
|
||||
// column: {
|
||||
// valueChange(context){
|
||||
// return api.UpdateObj(context.row)
|
||||
// },
|
||||
// component: {
|
||||
// name: 'fs-dict-switch',
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// is_update: {
|
||||
// title: '编辑时显示',
|
||||
// search: {
|
||||
// show: true,
|
||||
// },
|
||||
// type: 'dict-switch',
|
||||
// dict: dict({
|
||||
// data: [
|
||||
// { value: true, label: '启用' },
|
||||
// { value: false, label: '禁用' },
|
||||
// ],
|
||||
// }),
|
||||
// form: {
|
||||
// value: true,
|
||||
// },
|
||||
// column: {
|
||||
// component: {
|
||||
// name: 'fs-dict-switch',
|
||||
// onChange: compute((context) => {
|
||||
// //动态onChange方法测试
|
||||
// return () => {
|
||||
// console.log('onChange', context.row.switch);
|
||||
// };
|
||||
// }),
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// is_query: {
|
||||
// title: '列表中显示',
|
||||
// type: 'dict-switch',
|
||||
// dict: dict({
|
||||
// data: [
|
||||
// { value: true, label: '启用' },
|
||||
// { value: false, label: '禁用' },
|
||||
// ],
|
||||
// }),
|
||||
// form: {
|
||||
// value: true,
|
||||
// },
|
||||
// column: {
|
||||
// component: {
|
||||
// name: 'fs-dict-switch',
|
||||
// onChange: compute((context) => {
|
||||
// //动态onChange方法测试
|
||||
// return () => {
|
||||
// console.log('onChange', context.row.switch);
|
||||
// };
|
||||
// }),
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="路由地址" prop="web_path">
|
||||
<el-form-item label="路由地址" prop="web_path">
|
||||
<el-input v-model="menuFormData.web_path" placeholder="请输入路由地址,请以/开头" />
|
||||
</el-form-item>
|
||||
|
||||
@@ -56,6 +56,16 @@
|
||||
<el-switch v-model="menuFormData.is_link" width="60" inline-prompt active-text="是" inactive-text="否" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item required v-if="!menuFormData.is_catalog" label="是否固定">
|
||||
<el-switch v-model="menuFormData.is_affix" width="60" inline-prompt active-text="是" inactive-text="否" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item v-if="!menuFormData.is_catalog" required label="框外显示">
|
||||
<el-switch v-model="menuFormData.is_iframe" width="60" inline-prompt active-text="是" inactive-text="否" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-form-item label="备注">
|
||||
@@ -81,8 +91,8 @@
|
||||
<el-input v-model="menuFormData.component_name" placeholder="请输入组件名称" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="!menuFormData.is_catalog && menuFormData.is_link" label="Url" prop="web_path">
|
||||
<el-input v-model="menuFormData.web_path" placeholder="请输入Url" />
|
||||
<el-form-item v-if="!menuFormData.is_catalog && menuFormData.is_link" label="外链接" prop="link_url">
|
||||
<el-input v-model="menuFormData.link_url" placeholder="请输入外链接地址" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="!menuFormData.is_catalog" label="缓存">
|
||||
@@ -129,8 +139,7 @@ const defaultTreeProps: any = {
|
||||
};
|
||||
const validateWebPath = (rule: any, value: string, callback: Function) => {
|
||||
let pattern = /^\/.*?/;
|
||||
let patternUrl = /http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/;
|
||||
const reg = menuFormData.is_link ? patternUrl.test(value) : pattern.test(value);
|
||||
const reg = pattern.test(value);
|
||||
if (reg) {
|
||||
callback();
|
||||
} else {
|
||||
@@ -138,6 +147,17 @@ const validateWebPath = (rule: any, value: string, callback: Function) => {
|
||||
}
|
||||
};
|
||||
|
||||
const validateLinkUrl = (rule: any, value: string, callback: Function) => {
|
||||
let pattern = /^\/.*?/;
|
||||
let patternUrl = /http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/;
|
||||
const reg = pattern.test(value) || patternUrl.test(value)
|
||||
if (reg) {
|
||||
callback();
|
||||
} else {
|
||||
callback(new Error('请输入正确的地址'));
|
||||
}
|
||||
};
|
||||
|
||||
const props = withDefaults(defineProps<IProps>(), {
|
||||
initFormData: () => null,
|
||||
treeData: () => [],
|
||||
@@ -152,6 +172,7 @@ const rules = reactive<FormRules>({
|
||||
name: [{ required: true, message: '菜单名称必填', trigger: 'blur' }],
|
||||
component: [{ required: true, message: '请输入组件地址', trigger: 'blur' }],
|
||||
component_name: [{ required: true, message: '请输入组件名称', trigger: 'blur' }],
|
||||
link_url: [{ required: true, message: '请输入外链接地址',validator:validateLinkUrl, trigger: 'blur' }],
|
||||
});
|
||||
|
||||
let deptDefaultList = ref<MenuTreeItemType[]>([]);
|
||||
@@ -168,6 +189,9 @@ let menuFormData = reactive<MenuFormDataType>({
|
||||
description: '',
|
||||
is_catalog: false,
|
||||
is_link: false,
|
||||
is_iframe: false,
|
||||
is_affix: false,
|
||||
link_url:''
|
||||
});
|
||||
let menuBtnLoading = ref(false);
|
||||
|
||||
@@ -186,6 +210,9 @@ const setMenuFormData = () => {
|
||||
menuFormData.description = props.initFormData?.description || '';
|
||||
menuFormData.is_catalog = !!props.initFormData.is_catalog;
|
||||
menuFormData.is_link = !!props.initFormData.is_link;
|
||||
menuFormData.is_iframe =!!props.initFormData.is_iframe;
|
||||
menuFormData.is_affix =!!props.initFormData.is_affix;
|
||||
menuFormData.link_url =props.initFormData.link_url;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -44,6 +44,9 @@ export interface MenuTreeItemType {
|
||||
visible: boolean;
|
||||
creator: string;
|
||||
parent: number | string;
|
||||
is_iframe:boolean;
|
||||
is_affix:boolean;
|
||||
link_url:string;
|
||||
}
|
||||
|
||||
export interface MenuFormDataType {
|
||||
@@ -60,4 +63,7 @@ export interface MenuFormDataType {
|
||||
description: string;
|
||||
is_catalog: boolean;
|
||||
is_link: boolean;
|
||||
}
|
||||
is_iframe:boolean;
|
||||
is_affix:boolean;
|
||||
link_url: string;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user