Your Name
2022-07-13 656696be4b70513e94f1341db8d1c2d3f43b3e6d
登录跳转首页
已修改20个文件
已删除1个文件
1959 ■■■■ 文件已修改
.env.development 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
.idea/inspectionProfiles/Project_Default.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/department/index.ts 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/login/index.ts 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/menu/index.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/role/index.ts 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/iconSelector/index.vue 498 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layout/navBars/breadcrumb/user.vue 561 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/backEnd.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/index.ts 135 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/request.ts 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/doublePreventSystem/riskLevelManage/productionDevice/components/productionDeviceDialog.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/doublePreventSystem/riskLevelManage/riskControlMeasure/components/riskControlMeasureDialog.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/doublePreventSystem/riskLevelManage/safetyRiskAnalyseUnit/components/safetyRiskAnalyseUnitDialog.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/homeMenu/homeMenu.vue 573 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/loginPage/component/accountLogin.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/department/component/deptDialog.vue 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/department/index.vue 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/menu/component/menuDialog.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/role/component/roleDialog.vue 25 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/role/index.vue 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.env.development
@@ -2,6 +2,6 @@
ENV = 'development'
# 本地环境接口地址
VITE_API_URL = 'http://192.168.0.14:8008'
VITE_API_URL = 'http://192.168.0.35:8008'
# VITE_API_URL = 'http://192.168.0.8:8008'
.idea/inspectionProfiles/Project_Default.xml
文件已删除
src/api/department/index.ts
@@ -2,14 +2,14 @@
export function departmentApi() {
    return {
        // v1
        // v2
        getDepartmentList: () => {
            return request({
                url: `/department/list`,
                method: 'post'
            });
        },
        // v1
        // v2
        addDepartment: (data: object) => {
            return request({
                url: `/department/add`,
@@ -17,13 +17,21 @@
                data: data
            });
        },
        // v1
        // v2
        modDepartment: (data: object) => {
            return request({
                url: `/department/mod`,
                method: 'post',
                data: data
            });
        }
        },
        // v1
        deleteDepartment: (value?: object) => {
            return request({
                url: `/department/del`,
                method: 'post',
                data: value
            });
        },
    };
}
src/api/login/index.ts
@@ -16,11 +16,10 @@
            });
        },
        // v1
        signOut: (params: object) => {
        signOut: () => {
            return request({
                url: '/user/signOut',
                method: 'post',
                data: params
                url: '/auth/logout',
                method: 'post'
            });
        }
    };
src/api/menu/index.ts
@@ -8,7 +8,7 @@
 */
export function useMenuApi() {
    return {
        // v1
        // v2
        getMenuAdmin: (value?: string) => {
            return request({
                url: `/auth/menu?projectId= ${value}`,
src/api/role/index.ts
@@ -2,14 +2,14 @@
export function useRoleApi() {
    return {
        // v1
        // v2
        getRoleList: () => {
            return request({
                url: `/role/list`,
                method: 'post'
            });
        },
        // v1
        // v2
        addRole: (data: object) => {
            return request({
                url: `/role/add`,
@@ -17,13 +17,21 @@
                data: data
            });
        },
        // v1
        // v2
        modRole: (data: object) => {
            return request({
                url: `/role/mod`,
                method: 'post',
                data: data
            });
        }
        },
        // v2
        deleteRole: (value?: object) => {
            return request({
                url: `/role/del`,
                method: 'post',
                data: value
            });
        },
    };
}
src/components/iconSelector/index.vue
@@ -1,252 +1,252 @@
<template>
    <div class="icon-selector w100 h100">
        <el-popover
            placement="bottom"
            :width="fontIconWidth"
            trigger="click"
            transition="el-zoom-in-top"
            popper-class="icon-selector-popper"
            @show="onPopoverShow"
        >
            <template #reference>
                <el-input
                    v-model="fontIconSearch"
                    :placeholder="fontIconPlaceholder"
                    :clearable="clearable"
                    :disabled="disabled"
                    :size="size"
                    ref="inputWidthRef"
                    @clear="onClearFontIcon"
                    @focus="onIconFocus"
                    @blur="onIconBlur"
                >
                    <template #prepend>
                        <SvgIcon
                            :name="fontIconPrefix === '' ? prepend : fontIconPrefix"
                            class="font14"
                            v-if="fontIconPrefix === '' ? prepend?.indexOf('ele-') > -1 : fontIconPrefix?.indexOf('ele-') > -1"
                        />
                        <i v-else :class="fontIconPrefix === '' ? prepend : fontIconPrefix" class="font14"></i>
                    </template>
                </el-input>
            </template>
            <template #default>
                <div class="icon-selector-warp">
                    <div class="icon-selector-warp-title flex">
                        <div class="flex-auto">{{ title }}</div>
                        <div class="icon-selector-warp-title-tab" v-if="type === 'all'">
                            <span :class="{ 'span-active': fontIconType === 'ali' }" @click="onIconChange('ali')" class="ml10" title="iconfont 图标">ali</span>
                            <span :class="{ 'span-active': fontIconType === 'ele' }" @click="onIconChange('ele')" class="ml10" title="elementPlus 图标">ele</span>
                            <span :class="{ 'span-active': fontIconType === 'awe' }" @click="onIconChange('awe')" class="ml10" title="fontawesome 图标">awe</span>
                        </div>
                    </div>
                    <div class="icon-selector-warp-row">
                        <el-scrollbar ref="selectorScrollbarRef">
                            <el-row :gutter="10" v-if="fontIconSheetsFilterList.length > 0">
                                <el-col :xs="6" :sm="4" :md="4" :lg="4" :xl="4" @click="onColClick(v)" v-for="(v, k) in fontIconSheetsFilterList" :key="k">
                                    <div class="icon-selector-warp-item" :class="{ 'icon-selector-active': fontIconPrefix === v }">
                                        <div class="flex-margin">
                                            <div class="icon-selector-warp-item-value">
                                                <SvgIcon :name="v" />
                                            </div>
                                        </div>
                                    </div>
                                </el-col>
                            </el-row>
                            <el-empty :image-size="100" v-if="fontIconSheetsFilterList.length <= 0" :description="emptyDescription"></el-empty>
                        </el-scrollbar>
                    </div>
                </div>
            </template>
        </el-popover>
    </div>
</template>
<!--<template>-->
<!--    <div class="icon-selector w100 h100">-->
<!--        <el-popover-->
<!--            placement="bottom"-->
<!--            :width="fontIconWidth"-->
<!--            trigger="click"-->
<!--            transition="el-zoom-in-top"-->
<!--            popper-class="icon-selector-popper"-->
<!--            @show="onPopoverShow"-->
<!--        >-->
<!--            <template #reference>-->
<!--                <el-input-->
<!--                    v-model="fontIconSearch"-->
<!--                    :placeholder="fontIconPlaceholder"-->
<!--                    :clearable="clearable"-->
<!--                    :disabled="disabled"-->
<!--                    :size="size"-->
<!--                    ref="inputWidthRef"-->
<!--                    @clear="onClearFontIcon"-->
<!--                    @focus="onIconFocus"-->
<!--                    @blur="onIconBlur"-->
<!--                >-->
<!--                    <template #prepend>-->
<!--                        <SvgIcon-->
<!--                            :name="fontIconPrefix === '' ? prepend : fontIconPrefix"-->
<!--                            class="font14"-->
<!--                            v-if="fontIconPrefix === '' ? prepend?.indexOf('ele-') > -1 : fontIconPrefix?.indexOf('ele-') > -1"-->
<!--                        />-->
<!--                        <i v-else :class="fontIconPrefix === '' ? prepend : fontIconPrefix" class="font14"></i>-->
<!--                    </template>-->
<!--                </el-input>-->
<!--            </template>-->
<!--            <template #default>-->
<!--                <div class="icon-selector-warp">-->
<!--                    <div class="icon-selector-warp-title flex">-->
<!--                        <div class="flex-auto">{{ title }}</div>-->
<!--                        <div class="icon-selector-warp-title-tab" v-if="type === 'all'">-->
<!--                            <span :class="{ 'span-active': fontIconType === 'ali' }" @click="onIconChange('ali')" class="ml10" title="iconfont 图标">ali</span>-->
<!--                            <span :class="{ 'span-active': fontIconType === 'ele' }" @click="onIconChange('ele')" class="ml10" title="elementPlus 图标">ele</span>-->
<!--                            <span :class="{ 'span-active': fontIconType === 'awe' }" @click="onIconChange('awe')" class="ml10" title="fontawesome 图标">awe</span>-->
<!--                        </div>-->
<!--                    </div>-->
<!--                    <div class="icon-selector-warp-row">-->
<!--                        <el-scrollbar ref="selectorScrollbarRef">-->
<!--                            <el-row :gutter="10" v-if="fontIconSheetsFilterList.length > 0">-->
<!--                                <el-col :xs="6" :sm="4" :md="4" :lg="4" :xl="4" @click="onColClick(v)" v-for="(v, k) in fontIconSheetsFilterList" :key="k">-->
<!--                                    <div class="icon-selector-warp-item" :class="{ 'icon-selector-active': fontIconPrefix === v }">-->
<!--                                        <div class="flex-margin">-->
<!--                                            <div class="icon-selector-warp-item-value">-->
<!--                                                <SvgIcon :name="v" />-->
<!--                                            </div>-->
<!--                                        </div>-->
<!--                                    </div>-->
<!--                                </el-col>-->
<!--                            </el-row>-->
<!--                            <el-empty :image-size="100" v-if="fontIconSheetsFilterList.length <= 0" :description="emptyDescription"></el-empty>-->
<!--                        </el-scrollbar>-->
<!--                    </div>-->
<!--                </div>-->
<!--            </template>-->
<!--        </el-popover>-->
<!--    </div>-->
<!--</template>-->
<script lang="ts">
import { ref, toRefs, reactive, onMounted, nextTick, computed, watch, defineComponent } from 'vue';
import initIconfont from '/@/utils/getStyleSheets';
<!--<script lang="ts">-->
<!--import { ref, toRefs, reactive, onMounted, nextTick, computed, watch, defineComponent } from 'vue';-->
<!--import initIconfont from '/@/utils/getStyleSheets';-->
export default defineComponent({
    name: 'iconSelector',
    emits: ['update:modelValue', 'get', 'clear'],
    props: {
        // 输入框前置内容
        prepend: {
            type: String,
            default: () => 'ele-Pointer',
        },
        // 输入框占位文本
        placeholder: {
            type: String,
            default: () => '请输入内容搜索图标或者选择图标',
        },
        // 输入框占位文本
        size: {
            type: String,
            default: () => 'default',
        },
        // 弹窗标题
        title: {
            type: String,
            default: () => '请选择图标',
        },
        // icon 图标类型
        type: {
            type: String,
            default: () => 'ele',
        },
        // 禁用
        disabled: {
            type: Boolean,
            default: () => false,
        },
        // 是否可清空
        clearable: {
            type: Boolean,
            default: () => true,
        },
        // 自定义空状态描述文字
        emptyDescription: {
            type: String,
            default: () => '无相关图标',
        },
        // 双向绑定值,默认为 modelValue,
        // 参考:https://v3.cn.vuejs.org/guide/migration/v-model.html#%E8%BF%81%E7%A7%BB%E7%AD%96%E7%95%A5
        // 参考:https://v3.cn.vuejs.org/guide/component-custom-events.html#%E5%A4%9A%E4%B8%AA-v-model-%E7%BB%91%E5%AE%9A
        modelValue: String,
    },
    setup(props, { emit }) {
        const inputWidthRef = ref();
        const selectorScrollbarRef = ref();
        const state = reactive({
            fontIconPrefix: '',
            fontIconWidth: 0,
            fontIconSearch: '',
            fontIconTabsIndex: 0,
            fontIconSheetsList: [],
            fontIconPlaceholder: '',
            fontIconType: 'ali',
            fontIconShow: true,
        });
        // 处理 input 获取焦点时,modelValue 有值时,改变 input 的 placeholder 值
        const onIconFocus = () => {
            if (!props.modelValue) return false;
            state.fontIconSearch = '';
            state.fontIconPlaceholder = props.modelValue;
        };
        // 处理 input 失去焦点时,为空将清空 input 值,为点击选中图标时,将取原先值
        const onIconBlur = () => {
            setTimeout(() => {
                const icon = state.fontIconSheetsList.filter((icon: string) => icon === state.fontIconSearch);
                if (icon.length <= 0) state.fontIconSearch = '';
            }, 300);
        };
        // 处理 icon 双向绑定数值回显
        const initModeValueEcho = () => {
            if (props.modelValue === '') return ((<string | undefined>state.fontIconPlaceholder) = props.placeholder);
            (<string | undefined>state.fontIconPlaceholder) = props.modelValue;
            (<string | undefined>state.fontIconPrefix) = props.modelValue;
        };
        // 处理 icon type 类型为 all 时,类型 ali、ele、awe 回显问题
        const initFontIconTypeEcho = () => {
            if ((<any>props.modelValue)?.indexOf('iconfont') > -1) onIconChange('ali');
            else if ((<any>props.modelValue)?.indexOf('ele-') > -1) onIconChange('ele');
            else if ((<any>props.modelValue)?.indexOf('fa') > -1) onIconChange('awe');
            else onIconChange('ali');
        };
        // 图标搜索及图标数据显示
        const fontIconSheetsFilterList = computed(() => {
            if (!state.fontIconSearch) return state.fontIconSheetsList;
            let search = state.fontIconSearch.trim().toLowerCase();
            return state.fontIconSheetsList.filter((item: any) => {
                if (item.toLowerCase().indexOf(search) !== -1) return item;
            });
        });
        // 获取 input 的宽度
        const getInputWidth = () => {
            nextTick(() => {
                state.fontIconWidth = inputWidthRef.value.$el.offsetWidth;
            });
        };
        // 监听页面宽度改变
        const initResize = () => {
            window.addEventListener('resize', () => {
                getInputWidth();
            });
        };
        // 初始化数据
        const initFontIconData = async (type: string) => {
            state.fontIconSheetsList = [];
            if (type === 'ali') {
                await initIconfont.ali().then((res: any) => {
                    // 阿里字体图标使用 `iconfont xxx`
                    state.fontIconSheetsList = res.map((i: string) => `iconfont ${i}`);
                });
            } else if (type === 'ele') {
                await initIconfont.ele().then((res: any) => {
                    state.fontIconSheetsList = res;
                });
            } else if (type === 'awe') {
                await initIconfont.awe().then((res: any) => {
                    // fontawesome字体图标使用 `fa xxx`
                    state.fontIconSheetsList = res.map((i: string) => `fa ${i}`);
                });
            }
            // 初始化 input 的 placeholder
            // 参考(单项数据流):https://cn.vuejs.org/v2/guide/components-props.html?#%E5%8D%95%E5%90%91%E6%95%B0%E6%8D%AE%E6%B5%81
            state.fontIconPlaceholder = props.placeholder;
            // 初始化双向绑定回显
            initModeValueEcho();
        };
        // 图标点击切换
        const onIconChange = (type: string) => {
            state.fontIconType = type;
            initFontIconData(type);
        };
        // 获取当前点击的 icon 图标
        const onColClick = (v: any) => {
            state.fontIconPlaceholder = v;
            state.fontIconPrefix = v;
            emit('get', state.fontIconPrefix);
            emit('update:modelValue', state.fontIconPrefix);
        };
        // 清空当前点击的 icon 图标
        const onClearFontIcon = () => {
            state.fontIconPrefix = '';
            emit('clear', state.fontIconPrefix);
            emit('update:modelValue', state.fontIconPrefix);
        };
        // 监听 Popover 打开,用于双向绑定值回显
        const onPopoverShow = () => {
            initModeValueEcho();
            initFontIconTypeEcho();
        };
        // 页面加载时
        onMounted(() => {
            initModeValueEcho();
            initResize();
            getInputWidth();
        });
<!--export default defineComponent({-->
<!--    name: 'iconSelector',-->
<!--    emits: ['update:modelValue', 'get', 'clear'],-->
<!--    props: {-->
<!--        // 输入框前置内容-->
<!--        prepend: {-->
<!--            type: String,-->
<!--            default: () => 'ele-Pointer',-->
<!--        },-->
<!--        // 输入框占位文本-->
<!--        placeholder: {-->
<!--            type: String,-->
<!--            default: () => '请输入内容搜索图标或者选择图标',-->
<!--        },-->
<!--        // 输入框占位文本-->
<!--        size: {-->
<!--            type: String,-->
<!--            default: () => 'default',-->
<!--        },-->
<!--        // 弹窗标题-->
<!--        title: {-->
<!--            type: String,-->
<!--            default: () => '请选择图标',-->
<!--        },-->
<!--        // icon 图标类型-->
<!--        type: {-->
<!--            type: String,-->
<!--            default: () => 'ele',-->
<!--        },-->
<!--        // 禁用-->
<!--        disabled: {-->
<!--            type: Boolean,-->
<!--            default: () => false,-->
<!--        },-->
<!--        // 是否可清空-->
<!--        clearable: {-->
<!--            type: Boolean,-->
<!--            default: () => true,-->
<!--        },-->
<!--        // 自定义空状态描述文字-->
<!--        emptyDescription: {-->
<!--            type: String,-->
<!--            default: () => '无相关图标',-->
<!--        },-->
<!--        // 双向绑定值,默认为 modelValue,-->
<!--        // 参考:https://v3.cn.vuejs.org/guide/migration/v-model.html#%E8%BF%81%E7%A7%BB%E7%AD%96%E7%95%A5-->
<!--        // 参考:https://v3.cn.vuejs.org/guide/component-custom-events.html#%E5%A4%9A%E4%B8%AA-v-model-%E7%BB%91%E5%AE%9A-->
<!--        modelValue: String,-->
<!--    },-->
<!--    setup(props, { emit }) {-->
<!--        const inputWidthRef = ref();-->
<!--        const selectorScrollbarRef = ref();-->
<!--        const state = reactive({-->
<!--            fontIconPrefix: '',-->
<!--            fontIconWidth: 0,-->
<!--            fontIconSearch: '',-->
<!--            fontIconTabsIndex: 0,-->
<!--            fontIconSheetsList: [],-->
<!--            fontIconPlaceholder: '',-->
<!--            fontIconType: 'ali',-->
<!--            fontIconShow: true,-->
<!--        });-->
<!--        // 处理 input 获取焦点时,modelValue 有值时,改变 input 的 placeholder 值-->
<!--        const onIconFocus = () => {-->
<!--            if (!props.modelValue) return false;-->
<!--            state.fontIconSearch = '';-->
<!--            state.fontIconPlaceholder = props.modelValue;-->
<!--        };-->
<!--        // 处理 input 失去焦点时,为空将清空 input 值,为点击选中图标时,将取原先值-->
<!--        const onIconBlur = () => {-->
<!--            setTimeout(() => {-->
<!--                const icon = state.fontIconSheetsList.filter((icon: string) => icon === state.fontIconSearch);-->
<!--                if (icon.length <= 0) state.fontIconSearch = '';-->
<!--            }, 300);-->
<!--        };-->
<!--        // 处理 icon 双向绑定数值回显-->
<!--        const initModeValueEcho = () => {-->
<!--            if (props.modelValue === '') return ((<string | undefined>state.fontIconPlaceholder) = props.placeholder);-->
<!--            (<string | undefined>state.fontIconPlaceholder) = props.modelValue;-->
<!--            (<string | undefined>state.fontIconPrefix) = props.modelValue;-->
<!--        };-->
<!--        // 处理 icon type 类型为 all 时,类型 ali、ele、awe 回显问题-->
<!--        const initFontIconTypeEcho = () => {-->
<!--            if ((<any>props.modelValue)?.indexOf('iconfont') > -1) onIconChange('ali');-->
<!--            else if ((<any>props.modelValue)?.indexOf('ele-') > -1) onIconChange('ele');-->
<!--            else if ((<any>props.modelValue)?.indexOf('fa') > -1) onIconChange('awe');-->
<!--            else onIconChange('ali');-->
<!--        };-->
<!--        // 图标搜索及图标数据显示-->
<!--        const fontIconSheetsFilterList = computed(() => {-->
<!--            if (!state.fontIconSearch) return state.fontIconSheetsList;-->
<!--            let search = state.fontIconSearch.trim().toLowerCase();-->
<!--            return state.fontIconSheetsList.filter((item: any) => {-->
<!--                if (item.toLowerCase().indexOf(search) !== -1) return item;-->
<!--            });-->
<!--        });-->
<!--        // 获取 input 的宽度-->
<!--        const getInputWidth = () => {-->
<!--            nextTick(() => {-->
<!--                state.fontIconWidth = inputWidthRef.value.$el.offsetWidth;-->
<!--            });-->
<!--        };-->
<!--        // 监听页面宽度改变-->
<!--        const initResize = () => {-->
<!--            window.addEventListener('resize', () => {-->
<!--                getInputWidth();-->
<!--            });-->
<!--        };-->
<!--        // 初始化数据-->
<!--        const initFontIconData = async (type: string) => {-->
<!--            state.fontIconSheetsList = [];-->
<!--            if (type === 'ali') {-->
<!--                await initIconfont.ali().then((res: any) => {-->
<!--                    // 阿里字体图标使用 `iconfont xxx`-->
<!--                    state.fontIconSheetsList = res.map((i: string) => `iconfont ${i}`);-->
<!--                });-->
<!--            } else if (type === 'ele') {-->
<!--                await initIconfont.ele().then((res: any) => {-->
<!--                    state.fontIconSheetsList = res;-->
<!--                });-->
<!--            } else if (type === 'awe') {-->
<!--                await initIconfont.awe().then((res: any) => {-->
<!--                    // fontawesome字体图标使用 `fa xxx`-->
<!--                    state.fontIconSheetsList = res.map((i: string) => `fa ${i}`);-->
<!--                });-->
<!--            }-->
<!--            // 初始化 input 的 placeholder-->
<!--            // 参考(单项数据流):https://cn.vuejs.org/v2/guide/components-props.html?#%E5%8D%95%E5%90%91%E6%95%B0%E6%8D%AE%E6%B5%81-->
<!--            state.fontIconPlaceholder = props.placeholder;-->
<!--            // 初始化双向绑定回显-->
<!--            initModeValueEcho();-->
<!--        };-->
<!--        // 图标点击切换-->
<!--        const onIconChange = (type: string) => {-->
<!--            state.fontIconType = type;-->
<!--            initFontIconData(type);-->
<!--        };-->
<!--        // 获取当前点击的 icon 图标-->
<!--        const onColClick = (v: any) => {-->
<!--            state.fontIconPlaceholder = v;-->
<!--            state.fontIconPrefix = v;-->
<!--            emit('get', state.fontIconPrefix);-->
<!--            emit('update:modelValue', state.fontIconPrefix);-->
<!--        };-->
<!--        // 清空当前点击的 icon 图标-->
<!--        const onClearFontIcon = () => {-->
<!--            state.fontIconPrefix = '';-->
<!--            emit('clear', state.fontIconPrefix);-->
<!--            emit('update:modelValue', state.fontIconPrefix);-->
<!--        };-->
<!--        // 监听 Popover 打开,用于双向绑定值回显-->
<!--        const onPopoverShow = () => {-->
<!--            initModeValueEcho();-->
<!--            initFontIconTypeEcho();-->
<!--        };-->
<!--        // 页面加载时-->
<!--        onMounted(() => {-->
<!--            initModeValueEcho();-->
<!--            initResize();-->
<!--            getInputWidth();-->
<!--        });-->
        // 监听双向绑定 modelValue 的变化
        watch(
            () => props.modelValue,
            () => {
                initModeValueEcho();
            }
        );
        return {
            inputWidthRef,
            selectorScrollbarRef,
            fontIconSheetsFilterList,
            onColClick,
            onIconChange,
            onClearFontIcon,
            onIconFocus,
            onIconBlur,
            onPopoverShow,
            ...toRefs(state),
        };
    },
});
</script>
<!--        // 监听双向绑定 modelValue 的变化-->
<!--        watch(-->
<!--            () => props.modelValue,-->
<!--            () => {-->
<!--                initModeValueEcho();-->
<!--            }-->
<!--        );-->
<!--        return {-->
<!--            inputWidthRef,-->
<!--            selectorScrollbarRef,-->
<!--            fontIconSheetsFilterList,-->
<!--            onColClick,-->
<!--            onIconChange,-->
<!--            onClearFontIcon,-->
<!--            onIconFocus,-->
<!--            onIconBlur,-->
<!--            onPopoverShow,-->
<!--            ...toRefs(state),-->
<!--        };-->
<!--    },-->
<!--});-->
<!--</script>-->
src/layout/navBars/breadcrumb/user.vue
@@ -1,79 +1,85 @@
<template>
    <div class="layout-navbars-breadcrumb-user pr15" :style="{ flex: layoutUserFlexNum }">
        <el-dropdown :show-timeout="70" :hide-timeout="50" trigger="click" @command="onComponentSizeChange">
            <div class="layout-navbars-breadcrumb-user-icon">
                <i class="iconfont icon-ziti" :title="$t('message.user.title0')"></i>
            </div>
            <template #dropdown>
                <el-dropdown-menu>
                    <el-dropdown-item command="large" :disabled="disabledSize === 'large'">{{ $t('message.user.dropdownLarge') }}</el-dropdown-item>
                    <el-dropdown-item command="default" :disabled="disabledSize === 'default'">{{ $t('message.user.dropdownDefault') }}</el-dropdown-item>
                    <el-dropdown-item command="small" :disabled="disabledSize === 'small'">{{ $t('message.user.dropdownSmall') }}</el-dropdown-item>
                </el-dropdown-menu>
            </template>
        </el-dropdown>
        <el-dropdown :show-timeout="70" :hide-timeout="50" trigger="click" @command="onLanguageChange">
            <div class="layout-navbars-breadcrumb-user-icon">
                <i class="iconfont" :class="disabledI18n === 'en' ? 'icon-fuhao-yingwen' : 'icon-fuhao-zhongwen'" :title="$t('message.user.title1')"></i>
            </div>
            <template #dropdown>
                <el-dropdown-menu>
                    <el-dropdown-item command="zh-cn" :disabled="disabledI18n === 'zh-cn'">简体中文</el-dropdown-item>
                    <el-dropdown-item command="en" :disabled="disabledI18n === 'en'">English</el-dropdown-item>
                    <el-dropdown-item command="zh-tw" :disabled="disabledI18n === 'zh-tw'">繁體中文</el-dropdown-item>
                </el-dropdown-menu>
            </template>
        </el-dropdown>
        <div class="layout-navbars-breadcrumb-user-icon" @click="onSearchClick">
            <el-icon :title="$t('message.user.title2')">
                <ele-Search />
            </el-icon>
        </div>
        <div class="layout-navbars-breadcrumb-user-icon" @click="onLayoutSetingClick">
            <i class="icon-skin iconfont" :title="$t('message.user.title3')"></i>
        </div>
        <div class="layout-navbars-breadcrumb-user-icon">
            <el-popover placement="bottom" trigger="click" transition="el-zoom-in-top" :width="300" :persistent="false">
                <template #reference>
                    <el-badge :is-dot="true">
                        <el-icon :title="$t('message.user.title4')">
                            <ele-Bell />
                        </el-icon>
                    </el-badge>
                </template>
                <template #default>
                    <UserNews />
                </template>
            </el-popover>
        </div>
        <div class="layout-navbars-breadcrumb-user-icon mr10" @click="onScreenfullClick">
            <i
                class="iconfont"
                :title="isScreenfull ? $t('message.user.title6') : $t('message.user.title5')"
                :class="!isScreenfull ? 'icon-fullscreen' : 'icon-tuichuquanping'"
            ></i>
        </div>
        <el-dropdown :show-timeout="70" :hide-timeout="50" @command="onHandleCommandClick">
            <span class="layout-navbars-breadcrumb-user-link">
                <img :src="userInfos.photo" class="layout-navbars-breadcrumb-user-link-photo mr5" />
                {{ userInfos.userName }}
                <el-icon class="el-icon--right">
                    <ele-ArrowDown />
                </el-icon>
            </span>
            <template #dropdown>
                <el-dropdown-menu>
                    <el-dropdown-item command="/home">{{ $t('message.user.dropdown1') }}</el-dropdown-item>
                    <el-dropdown-item command="wareHouse">{{ $t('message.user.dropdown6') }}</el-dropdown-item>
                    <el-dropdown-item command="/personal">{{ $t('message.user.dropdown2') }}</el-dropdown-item>
                    <el-dropdown-item command="/404">{{ $t('message.user.dropdown3') }}</el-dropdown-item>
                    <el-dropdown-item command="/401">{{ $t('message.user.dropdown4') }}</el-dropdown-item>
                    <el-dropdown-item divided command="logOut">{{ $t('message.user.dropdown5') }}</el-dropdown-item>
                </el-dropdown-menu>
            </template>
        </el-dropdown>
        <Search ref="searchRef" />
    </div>
    <div class="layout-navbars-breadcrumb-user pr15" :style="{ flex: layoutUserFlexNum }">
        <el-dropdown :show-timeout="70" :hide-timeout="50" trigger="click" @command="onComponentSizeChange">
            <div class="layout-navbars-breadcrumb-user-icon">
                <i class="iconfont icon-ziti" :title="$t('message.user.title0')"></i>
            </div>
            <template #dropdown>
                <el-dropdown-menu>
                    <el-dropdown-item command="large" :disabled="disabledSize === 'large'">{{ $t('message.user.dropdownLarge') }}</el-dropdown-item>
                    <el-dropdown-item command="default" :disabled="disabledSize === 'default'">{{
                        $t('message.user.dropdownDefault')
                    }}</el-dropdown-item>
                    <el-dropdown-item command="small" :disabled="disabledSize === 'small'">{{ $t('message.user.dropdownSmall') }}</el-dropdown-item>
                </el-dropdown-menu>
            </template>
        </el-dropdown>
        <el-dropdown :show-timeout="70" :hide-timeout="50" trigger="click" @command="onLanguageChange">
            <div class="layout-navbars-breadcrumb-user-icon">
                <i
                    class="iconfont"
                    :class="disabledI18n === 'en' ? 'icon-fuhao-yingwen' : 'icon-fuhao-zhongwen'"
                    :title="$t('message.user.title1')"
                ></i>
            </div>
            <template #dropdown>
                <el-dropdown-menu>
                    <el-dropdown-item command="zh-cn" :disabled="disabledI18n === 'zh-cn'">简体中文</el-dropdown-item>
                    <el-dropdown-item command="en" :disabled="disabledI18n === 'en'">English</el-dropdown-item>
                    <el-dropdown-item command="zh-tw" :disabled="disabledI18n === 'zh-tw'">繁體中文</el-dropdown-item>
                </el-dropdown-menu>
            </template>
        </el-dropdown>
        <div class="layout-navbars-breadcrumb-user-icon" @click="onSearchClick">
            <el-icon :title="$t('message.user.title2')">
                <ele-Search />
            </el-icon>
        </div>
        <div class="layout-navbars-breadcrumb-user-icon" @click="onLayoutSetingClick">
            <i class="icon-skin iconfont" :title="$t('message.user.title3')"></i>
        </div>
        <div class="layout-navbars-breadcrumb-user-icon">
            <el-popover placement="bottom" trigger="click" transition="el-zoom-in-top" :width="300" :persistent="false">
                <template #reference>
                    <el-badge :is-dot="true">
                        <el-icon :title="$t('message.user.title4')">
                            <ele-Bell />
                        </el-icon>
                    </el-badge>
                </template>
                <template #default>
                    <UserNews />
                </template>
            </el-popover>
        </div>
        <div class="layout-navbars-breadcrumb-user-icon mr10" @click="onScreenfullClick">
            <i
                class="iconfont"
                :title="isScreenfull ? $t('message.user.title6') : $t('message.user.title5')"
                :class="!isScreenfull ? 'icon-fullscreen' : 'icon-tuichuquanping'"
            ></i>
        </div>
        <el-dropdown :show-timeout="70" :hide-timeout="50" @command="onHandleCommandClick">
            <span class="layout-navbars-breadcrumb-user-link">
                <img :src="userInfos.photo" class="layout-navbars-breadcrumb-user-link-photo mr5" />
                {{ userInfos.userName }}
                <el-icon class="el-icon--right">
                    <ele-ArrowDown />
                </el-icon>
            </span>
            <template #dropdown>
                <el-dropdown-menu>
                    <el-dropdown-item command="/homeMenu">{{ $t('message.user.dropdown1') }}</el-dropdown-item>
                    <!--                    <el-dropdown-item command="wareHouse">{{ $t('message.user.dropdown6') }}</el-dropdown-item>-->
                    <el-dropdown-item command="/personal">{{ $t('message.user.dropdown2') }}</el-dropdown-item>
                    <!--                    <el-dropdown-item command="/404">{{ $t('message.user.dropdown3') }}</el-dropdown-item>-->
                    <!--                    <el-dropdown-item command="/401">{{ $t('message.user.dropdown4') }}</el-dropdown-item>-->
                    <el-dropdown-item divided command="logOut">{{ $t('message.user.dropdown5') }}</el-dropdown-item>
                </el-dropdown-menu>
            </template>
        </el-dropdown>
        <Search ref="searchRef" />
    </div>
</template>
<script lang="ts">
@@ -89,210 +95,219 @@
import { Session, Local } from '/@/utils/storage';
import UserNews from '/@/layout/navBars/breadcrumb/userNews.vue';
import Search from '/@/layout/navBars/breadcrumb/search.vue';
import { useLoginApi } from '/@/api/login';
export default defineComponent({
    name: 'layoutBreadcrumbUser',
    components: { UserNews, Search },
    setup() {
        const { t } = useI18n();
        const { proxy } = <any>getCurrentInstance();
        const router = useRouter();
        const stores = useUserInfo();
        const storesThemeConfig = useThemeConfig();
        const { userInfos } = storeToRefs(stores);
        const { themeConfig } = storeToRefs(storesThemeConfig);
        const searchRef = ref();
        const state = reactive({
            isScreenfull: false,
            disabledI18n: 'zh-cn',
            disabledSize: 'large',
        });
        // 设置分割样式
        const layoutUserFlexNum = computed(() => {
            let num: string | number = '';
            const { layout, isClassicSplitMenu } = themeConfig.value;
            const layoutArr: string[] = ['defaults', 'columns'];
            if (layoutArr.includes(layout) || (layout === 'classic' && !isClassicSplitMenu)) num = '1';
            else num = '';
            return num;
        });
        // 全屏点击时
        const onScreenfullClick = () => {
            if (!screenfull.isEnabled) {
                ElMessage.warning('暂不不支持全屏');
                return false;
            }
            screenfull.toggle();
            screenfull.on('change', () => {
                if (screenfull.isFullscreen) state.isScreenfull = true;
                else state.isScreenfull = false;
            });
        };
        // 布局配置 icon 点击时
        const onLayoutSetingClick = () => {
            proxy.mittBus.emit('openSetingsDrawer');
        };
        // 下拉菜单点击时
        const onHandleCommandClick = (path: string) => {
            if (path === 'logOut') {
                ElMessageBox({
                    closeOnClickModal: false,
                    closeOnPressEscape: false,
                    title: t('message.user.logOutTitle'),
                    message: t('message.user.logOutMessage'),
                    showCancelButton: true,
                    confirmButtonText: t('message.user.logOutConfirm'),
                    cancelButtonText: t('message.user.logOutCancel'),
                    buttonSize: 'default',
                    beforeClose: (action, instance, done) => {
                        if (action === 'confirm') {
                            instance.confirmButtonLoading = true;
                            instance.confirmButtonText = t('message.user.logOutExit');
                            setTimeout(() => {
                                done();
                                setTimeout(() => {
                                    instance.confirmButtonLoading = false;
                                }, 300);
                            }, 700);
                        } else {
                            done();
                        }
                    },
                })
                    .then(async () => {
                        Session.clear(); // 清除缓存/token等
                        // 使用 reload 时,不需要调用 resetRoute() 重置路由
                        window.location.reload();
                    })
                    .catch(() => {});
            } else if (path === 'wareHouse') {
                window.open('https://gitee.com/lyt-top/vue-next-admin');
            } else {
                router.push(path);
            }
        };
        // 菜单搜索点击
        const onSearchClick = () => {
            searchRef.value.openSearch();
        };
        // 组件大小改变
        const onComponentSizeChange = (size: string) => {
            Local.remove('themeConfig');
            themeConfig.value.globalComponentSize = size;
            Local.set('themeConfig', themeConfig.value);
            initComponentSize();
            window.location.reload();
        };
        // 语言切换
        const onLanguageChange = (lang: string) => {
            Local.remove('themeConfig');
            themeConfig.value.globalI18n = lang;
            Local.set('themeConfig', themeConfig.value);
            proxy.$i18n.locale = lang;
            initI18n();
            other.useTitle();
        };
        // 设置 element plus 组件的国际化
        const setI18nConfig = (locale: string) => {
            proxy.mittBus.emit('getI18nConfig', proxy.$i18n.messages[locale]);
        };
        // 初始化言语国际化
        const initI18n = () => {
            switch (Local.get('themeConfig').globalI18n) {
                case 'zh-cn':
                    state.disabledI18n = 'zh-cn';
                    setI18nConfig('zh-cn');
                    break;
                case 'en':
                    state.disabledI18n = 'en';
                    setI18nConfig('en');
                    break;
                case 'zh-tw':
                    state.disabledI18n = 'zh-tw';
                    setI18nConfig('zh-tw');
                    break;
            }
        };
        // 初始化全局组件大小
        const initComponentSize = () => {
            switch (Local.get('themeConfig').globalComponentSize) {
                case 'large':
                    state.disabledSize = 'large';
                    break;
                case 'default':
                    state.disabledSize = 'default';
                    break;
                case 'small':
                    state.disabledSize = 'small';
                    break;
            }
        };
        // 页面加载时
        onMounted(() => {
            if (Local.get('themeConfig')) {
                initI18n();
                initComponentSize();
            }
        });
        return {
            userInfos,
            onLayoutSetingClick,
            onHandleCommandClick,
            onScreenfullClick,
            onSearchClick,
            onComponentSizeChange,
            onLanguageChange,
            searchRef,
            layoutUserFlexNum,
            ...toRefs(state),
        };
    },
    name: 'layoutBreadcrumbUser',
    components: { UserNews, Search },
    setup() {
        const { t } = useI18n();
        const { proxy } = <any>getCurrentInstance();
        const router = useRouter();
        const stores = useUserInfo();
        const storesThemeConfig = useThemeConfig();
        const { userInfos } = storeToRefs(stores);
        const { themeConfig } = storeToRefs(storesThemeConfig);
        const searchRef = ref();
        const state = reactive({
            isScreenfull: false,
            disabledI18n: 'zh-cn',
            disabledSize: 'large'
        });
        // 设置分割样式
        const layoutUserFlexNum = computed(() => {
            let num: string | number = '';
            const { layout, isClassicSplitMenu } = themeConfig.value;
            const layoutArr: string[] = ['defaults', 'columns'];
            if (layoutArr.includes(layout) || (layout === 'classic' && !isClassicSplitMenu)) num = '1';
            else num = '';
            return num;
        });
        // 全屏点击时
        const onScreenfullClick = () => {
            if (!screenfull.isEnabled) {
                ElMessage.warning('暂不不支持全屏');
                return false;
            }
            screenfull.toggle();
            screenfull.on('change', () => {
                if (screenfull.isFullscreen) state.isScreenfull = true;
                else state.isScreenfull = false;
            });
        };
        // 布局配置 icon 点击时
        const onLayoutSetingClick = () => {
            proxy.mittBus.emit('openSetingsDrawer');
        };
        // 下拉菜单点击时
        const onHandleCommandClick = (path: string) => {
            if (path === 'logOut') {
                ElMessageBox({
                    closeOnClickModal: false,
                    closeOnPressEscape: false,
                    title: t('message.user.logOutTitle'),
                    message: t('message.user.logOutMessage'),
                    showCancelButton: true,
                    confirmButtonText: t('message.user.logOutConfirm'),
                    cancelButtonText: t('message.user.logOutCancel'),
                    buttonSize: 'default',
                    beforeClose: (action, instance, done) => {
                        if (action === 'confirm') {
                            instance.confirmButtonLoading = true;
                            instance.confirmButtonText = t('message.user.logOutExit');
                            setTimeout(() => {
                                done();
                                setTimeout(() => {
                                    instance.confirmButtonLoading = false;
                                }, 300);
                            }, 700);
                        } else {
                            done();
                        }
                    }
                })
                    .then(async () => {
                        let res = await useLoginApi().signOut();
                        if (res.data.code === '200') {
                            Session.clear(); // 清除缓存/token等
                            // 使用 reload 时,不需要调用 resetRoute() 重置路由
                            window.location.reload();
                        } else {
                            ElMessage({
                                type: 'warning',
                                message: res.data.msg
                            });
                        }
                    })
                    .catch(() => {});
            } else if (path === 'wareHouse') {
                window.open('https://gitee.com/lyt-top/vue-next-admin');
            } else {
                router.push(path);
            }
        };
        // 菜单搜索点击
        const onSearchClick = () => {
            searchRef.value.openSearch();
        };
        // 组件大小改变
        const onComponentSizeChange = (size: string) => {
            Local.remove('themeConfig');
            themeConfig.value.globalComponentSize = size;
            Local.set('themeConfig', themeConfig.value);
            initComponentSize();
            window.location.reload();
        };
        // 语言切换
        const onLanguageChange = (lang: string) => {
            Local.remove('themeConfig');
            themeConfig.value.globalI18n = lang;
            Local.set('themeConfig', themeConfig.value);
            proxy.$i18n.locale = lang;
            initI18n();
            other.useTitle();
        };
        // 设置 element plus 组件的国际化
        const setI18nConfig = (locale: string) => {
            proxy.mittBus.emit('getI18nConfig', proxy.$i18n.messages[locale]);
        };
        // 初始化言语国际化
        const initI18n = () => {
            switch (Local.get('themeConfig').globalI18n) {
                case 'zh-cn':
                    state.disabledI18n = 'zh-cn';
                    setI18nConfig('zh-cn');
                    break;
                case 'en':
                    state.disabledI18n = 'en';
                    setI18nConfig('en');
                    break;
                case 'zh-tw':
                    state.disabledI18n = 'zh-tw';
                    setI18nConfig('zh-tw');
                    break;
            }
        };
        // 初始化全局组件大小
        const initComponentSize = () => {
            switch (Local.get('themeConfig').globalComponentSize) {
                case 'large':
                    state.disabledSize = 'large';
                    break;
                case 'default':
                    state.disabledSize = 'default';
                    break;
                case 'small':
                    state.disabledSize = 'small';
                    break;
            }
        };
        // 页面加载时
        onMounted(() => {
            if (Local.get('themeConfig')) {
                initI18n();
                initComponentSize();
            }
        });
        return {
            userInfos,
            onLayoutSetingClick,
            onHandleCommandClick,
            onScreenfullClick,
            onSearchClick,
            onComponentSizeChange,
            onLanguageChange,
            searchRef,
            layoutUserFlexNum,
            ...toRefs(state)
        };
    }
});
</script>
<style scoped lang="scss">
.layout-navbars-breadcrumb-user {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    &-link {
        height: 100%;
        display: flex;
        align-items: center;
        white-space: nowrap;
        &-photo {
            width: 25px;
            height: 25px;
            border-radius: 100%;
        }
    }
    &-icon {
        padding: 0 10px;
        cursor: pointer;
        color: var(--next-bg-topBarColor);
        height: 50px;
        line-height: 50px;
        display: flex;
        align-items: center;
        &:hover {
            background: var(--next-color-user-hover);
            i {
                display: inline-block;
                animation: logoAnimation 0.3s ease-in-out;
            }
        }
    }
    ::v-deep(.el-dropdown) {
        color: var(--next-bg-topBarColor);
    }
    ::v-deep(.el-badge) {
        height: 40px;
        line-height: 40px;
        display: flex;
        align-items: center;
    }
    ::v-deep(.el-badge__content.is-fixed) {
        top: 12px;
    }
    display: flex;
    align-items: center;
    justify-content: flex-end;
    &-link {
        height: 100%;
        display: flex;
        align-items: center;
        white-space: nowrap;
        &-photo {
            width: 25px;
            height: 25px;
            border-radius: 100%;
        }
    }
    &-icon {
        padding: 0 10px;
        cursor: pointer;
        color: var(--next-bg-topBarColor);
        height: 50px;
        line-height: 50px;
        display: flex;
        align-items: center;
        &:hover {
            background: var(--next-color-user-hover);
            i {
                display: inline-block;
                animation: logoAnimation 0.3s ease-in-out;
            }
        }
    }
    ::v-deep(.el-dropdown) {
        color: var(--next-bg-topBarColor);
    }
    ::v-deep(.el-badge) {
        height: 40px;
        line-height: 40px;
        display: flex;
        align-items: center;
    }
    ::v-deep(.el-badge__content.is-fixed) {
        top: 12px;
    }
}
</style>
src/router/backEnd.ts
@@ -30,7 +30,7 @@
    if (window.nextLoading === undefined) NextLoading.start();
    if (!Session.get('token')) return false;
    const res = await getBackEndControlRoutes(Session.get('projectId') === null ? '' : Session.get('projectId'));
    useRequestOldRoutes().setRequestOldRoutes(JSON.parse(JSON.stringify(res.data.data)));
    await useRequestOldRoutes().setRequestOldRoutes(JSON.parse(JSON.stringify(res.data.data)));
    dynamicRoutes[0].children = await backEndComponent(res.data.data);
    await setAddRoute();
    await setFilterMenuAndCacheTagsViewRoutes();
src/router/index.ts
@@ -10,9 +10,7 @@
import { staticRoutes } from '/@/router/route';
import { initFrontEndControlRoutes } from '/@/router/frontEnd';
import { initBackEndControlRoutes } from '/@/router/backEnd';
import {useUserInfo} from "/@/stores/userInfo";
import { useUserInfo } from '/@/stores/userInfo';
// 读取 `/src/stores/themeConfig.ts` 是否开启后端控制路由配置
const storesThemeConfig = useThemeConfig(pinia);
@@ -20,89 +18,86 @@
const { isRequestRoutes } = themeConfig.value;
if (isRequestRoutes) staticRoutes.splice(0, 1);
export const router = createRouter({
    history: createWebHashHistory(),
    routes: staticRoutes,
    history: createWebHashHistory(),
    routes: staticRoutes
});
export function formatFlatteningRoutes(arr: any) {
    if (arr.length <= 0) return false;
    for (let i = 0; i < arr.length; i++) {
        if (arr[i].children) {
            arr = arr.slice(0, i + 1).concat(arr[i].children, arr.slice(i + 1));
        }
    }
    return arr;
    if (arr.length <= 0) return false;
    for (let i = 0; i < arr.length; i++) {
        if (arr[i].children) {
            arr = arr.slice(0, i + 1).concat(arr[i].children, arr.slice(i + 1));
        }
    }
    return arr;
}
export function formatTwoStageRoutes(arr: any) {
    if (arr.length <= 0) return false;
    const newArr: 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: [] });
        } else {
            // 判断是否是动态路由(xx/:id/:name),用于 tagsView 等中使用
            // 修复:https://gitee.com/lyt-top/vue-next-admin/issues/I3YX6G
            if (v.path.indexOf('/:') > -1) {
                v.meta['isDynamic'] = true;
                v.meta['isDynamicPath'] = v.path;
            }
            newArr[0].children.push({ ...v });
            // 存 name 值,keep-alive 中 include 使用,实现路由的缓存
            // 路径:/@/layout/routerView/parent.vue
            if (newArr[0].meta.isKeepAlive && v.meta.isKeepAlive) {
                cacheList.push(v.name);
                const stores = useKeepALiveNames(pinia);
                stores.setCacheKeepAlive(cacheList);
            }
        }
    });
    return newArr;
    if (arr.length <= 0) return false;
    const newArr: 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: [] });
        } else {
            // 判断是否是动态路由(xx/:id/:name),用于 tagsView 等中使用
            // 修复:https://gitee.com/lyt-top/vue-next-admin/issues/I3YX6G
            if (v.path.indexOf('/:') > -1) {
                v.meta['isDynamic'] = true;
                v.meta['isDynamicPath'] = v.path;
            }
            newArr[0].children.push({ ...v });
            // 存 name 值,keep-alive 中 include 使用,实现路由的缓存
            // 路径:/@/layout/routerView/parent.vue
            if (newArr[0].meta.isKeepAlive && v.meta.isKeepAlive) {
                cacheList.push(v.name);
                const stores = useKeepALiveNames(pinia);
                stores.setCacheKeepAlive(cacheList);
            }
        }
    });
    return newArr;
}
// isRequestRoutes 为 true,则开启后端控制路由,路径:`/src/stores/themeConfig.ts`
if (!isRequestRoutes) initFrontEndControlRoutes();
// // isRequestRoutes 为 true,则开启后端控制路由,路径:`/src/stores/themeConfig.ts`
// if (!isRequestRoutes) initFrontEndControlRoutes();
// 路由加载前
router.beforeEach(async (to, from, next) => {
    NProgress.configure({ showSpinner: false });
    if (to.meta.title) NProgress.start();
    const token = Session.get('token');
    if (to.path === '/login' && !token) {
        next();
        NProgress.done();
    } else {
        if (!token) {
            next(`/login?redirect=${to.path}&params=${JSON.stringify(to.query ? to.query : to.params)}`);
            Session.clear();
            NProgress.done();
        } else if (token && to.path === '/login') {
            next('/home');
            NProgress.done();
        } else {
            const storesRoutesList = useRoutesList(pinia);
            const { routesList } = storeToRefs(storesRoutesList);
            if (routesList.value.length === 0) {
                    // 后端控制路由:路由数据初始化,防止刷新时丢失
                await initBackEndControlRoutes();
                    // 动态添加路由:防止非首页刷新时跳转回首页的问题
                    // 确保 addRoute() 时动态添加的路由已经被完全加载上去
                next({ ...to, replace: true });
            } else {
                next();
            }
        }
    }
    NProgress.configure({ showSpinner: false });
    if (to.meta.title) NProgress.start();
    const token = Session.get('token');
    if (to.path === '/login' && !token) {
        next();
        NProgress.done();
    } else {
        if (!token) {
            next(`/login?redirect=${to.path}&params=${JSON.stringify(to.query ? to.query : to.params)}`);
            Session.clear();
            NProgress.done();
        } else if (token && to.path === '/login') {
            next('/homeMenu');
            NProgress.done();
        } else {
            const storesRoutesList = useRoutesList(pinia);
            const { routesList } = storeToRefs(storesRoutesList);
            if (routesList.value.length === 0) {
                // 后端控制路由:路由数据初始化,防止刷新时丢失
                await initBackEndControlRoutes();
                // 动态添加路由:防止非首页刷新时跳转回首页的问题
                // 确保 addRoute() 时动态添加的路由已经被完全加载上去
                next({ ...to, replace: true });
            } else {
                next();
            }
        }
    }
});
// 路由加载后
router.afterEach(() => {
    NProgress.done();
    NProgress.done();
});
// 导出路由
src/utils/request.ts
@@ -44,10 +44,16 @@
service.interceptors.response.use(
    (response) => {
        // 对响应数据做点什么
        if (response.data.code && (response.data.code === 'A0215' || response.data.code === 'A0214' || response.data.code === 'A0213')) {
        if (response.data.code && response.data.code === 'A0213') {
            ElMessage.error('用户uid不存在');
            setTimeout(() => {
                Session.clear();
                window.location.href = '/';
            }, 2000);
        } else if (response.data.code && response.data.code === 'A0215') {
            ElMessage.error('token失效');
            Session.clear();
            window.location.href = '/';
            return Promise.reject(response);
        }
        // if(response.data.code && response.data.code !== '200'){
        return Promise.resolve(response);
src/views/doublePreventSystem/riskLevelManage/productionDevice/components/productionDeviceDialog.vue
@@ -23,7 +23,7 @@
                        <el-form-item label="所属部门" prop="depId">
                            <el-cascader
                                :options="departmentList"
                                :props="{ emitPath: false, checkStrictly: true, value: 'id', label: 'name' }"
                                :props="{ emitPath: false, checkStrictly: true, value: 'depId', label: 'depName' }"
                                placeholder="请选择部门"
                                clearable
                                filterable
src/views/doublePreventSystem/riskLevelManage/riskControlMeasure/components/riskControlMeasureDialog.vue
@@ -194,7 +194,6 @@
        };
        const changeClassifyTwoList = () => {
            debugger;
            state.riskControlMeasureForm.classify2 = null;
            state.classifyTwoList = [];
            state.classifyTwoList = state.classifyTwoListAll.filter((item: any) => item.parentId === state.riskControlMeasureForm.classify1);
src/views/doublePreventSystem/riskLevelManage/safetyRiskAnalyseUnit/components/safetyRiskAnalyseUnitDialog.vue
@@ -49,7 +49,7 @@
                            <el-cascader
                                @change="achieveUserList"
                                :options="departmentList"
                                :props="{ emitPath: false, checkStrictly: true, value: 'id', label: 'name' }"
                                :props="{ emitPath: false, checkStrictly: true, value: 'depId', label: 'depName' }"
                                placeholder="请选择部门"
                                clearable
                                filterable
src/views/homeMenu/homeMenu.vue
@@ -1,53 +1,91 @@
<template>
    <div class="login-container">
        <div class="topPanel">
            <div class="topPanelCont">
                <div class="topTit">
                    <div>新疆国泰新华</div>
                    <span></span>
                    <div>安全风险预警监测系统</div>
                </div>
                <div class="userInfo">
                    <div class="avator">
                        <img src="../../assets/menu/admin.png">
                        <div>admin</div>
                    </div>
                    <span></span>
                    <div class="loginOut" @click="onLoginOut">退出登录</div>
                </div>
            </div>
        </div>
    <div class="login-container">
        <div class="topPanel">
            <div class="topPanelCont">
                <div class="topTit">
                    <div>新疆国泰新华</div>
                    <span></span>
                    <div>安全风险预警监测系统</div>
                </div>
                <div class="userInfo">
                    <div class="avator">
                        <img src="../../assets/menu/admin.png" />
                        <div>admin</div>
                    </div>
                    <span></span>
                    <div class="loginOut" @click="onLoginOut">退出登录</div>
                </div>
            </div>
        </div>
        <div class="menuGrid">
            <div class="gridCont">
                <el-row :gutter="20">
                    <el-col :span="6"><div class="grid-content" @click="renderMenu('2')"><div class="itemTit">双重预防系统</div><img class="iconImg" src="../../assets/menu/icon4.png"><img class="bgImg" src="../../assets/menu/card4.png"></div></el-col>
                    <el-col :span="6"><div class="grid-content" @click="renderMenu('3')"><div class="itemTit">特殊作业系统</div><img class="iconImg" src="../../assets/menu/icon7.png"><img class="bgImg" src="../../assets/menu/card7.png"></div></el-col>
                    <el-col :span="6"><div class="grid-content" @click="renderMenu('4')"><div class="itemTit">智能巡检系统</div><img class="iconImg" src="../../assets/menu/icon8.png"><img class="bgImg" src="../../assets/menu/card8.png"></div></el-col>
                    <el-col :span="6">
                        <div class="grid-content" @click="renderMenu('5')">
                            <div class="itemTit">安全风险综合
                                <br>预警预报平台
                            </div>
                            <img class="iconImg" src="../../assets/menu/icon1.png">
                            <img class="bgImg" src="../../assets/menu/card1.png">
                        </div>
                    </el-col>
                </el-row>
                <el-row :gutter="20">
                    <el-col :span="6"><div class="grid-content grid-content-2" @click="renderMenu('6')"><div class="itemTit">应急管理系统</div><img class="iconImg" src="../../assets/menu/icon5.png"><img class="bgImg" src="../../assets/menu/card5.png"></div></el-col>
                    <el-col :span="6"><div class="grid-content grid-content-2" @click="renderMenu('7')"><div class="itemTit">目标责任管理系统</div><img class="iconImg" src="../../assets/menu/icon2.png"><img class="bgImg" src="../../assets/menu/card2.png"></div></el-col>
                    <el-col :span="6"><div class="grid-content grid-content-2" @click="renderMenu('8')"><div class="itemTit">事故管理系统</div><img class="iconImg" src="../../assets/menu/icon6.png"><img class="bgImg" src="../../assets/menu/card6.png"></div></el-col>
                    <el-col :span="6"><div class="grid-content grid-content-2" @click="renderMenu('9')"><div class="itemTit">设备综合管控系统</div><img class="iconImg" src="../../assets/menu/icon9.png"><img class="bgImg" src="../../assets/menu/card9.png"></div></el-col>
                </el-row>
                <el-row :gutter="20">
                    <el-col :span="6"><div class="grid-content grid-content-3" @click="renderMenu('10')"><div class="itemTit">安全知识图谱系统</div><img class="iconImg" src="../../assets/menu/icon3.png"><img class="bgImg" src="../../assets/menu/card3.png"></div></el-col>
                    <el-col :span="9"><div class="grid-content grid-content-3" @click="renderMenu('11')"><div class="itemTit">危险化学品全生命周期安全<br>管理系统</div><img class="iconImg" src="../../assets/menu/icon10.png"><img class="bgImg" src="../../assets/menu/card10.png"></div></el-col>
                    <el-col :span="9"><div class="grid-content grid-content-3" @click="renderMenu('1')"><div class="itemTit">基础数据权限管理系统</div><img class="iconImg" src="../../assets/menu/icon11.png"><img class="bgImg" src="../../assets/menu/card11.png"></div></el-col>
                </el-row>
            </div>
        </div>
    </div>
        <div class="menuGrid">
            <div class="gridCont">
                <el-row :gutter="20">
                    <el-col :span="6"
                        ><div class="grid-content" v-throttle @click="renderMenu('2')">
                            <div class="itemTit">双重预防系统</div>
                            <img class="iconImg" src="../../assets/menu/icon4.png" /><img class="bgImg" src="../../assets/menu/card4.png" /></div
                    ></el-col>
                    <el-col :span="6"
                        ><div class="grid-content" v-throttle @click="renderMenu('3')">
                            <div class="itemTit">特殊作业系统</div>
                            <img class="iconImg" src="../../assets/menu/icon7.png" /><img class="bgImg" src="../../assets/menu/card7.png" /></div
                    ></el-col>
                    <el-col :span="6"
                        ><div class="grid-content" v-throttle @click="renderMenu('4')">
                            <div class="itemTit">智能巡检系统</div>
                            <img class="iconImg" src="../../assets/menu/icon8.png" /><img class="bgImg" src="../../assets/menu/card8.png" /></div
                    ></el-col>
                    <el-col :span="6">
                        <div class="grid-content" v-throttle @click="renderMenu('5')">
                            <div class="itemTit">安全风险综合 <br />预警预报平台</div>
                            <img class="iconImg" src="../../assets/menu/icon1.png" />
                            <img class="bgImg" src="../../assets/menu/card1.png" />
                        </div>
                    </el-col>
                </el-row>
                <el-row :gutter="20">
                    <el-col :span="6"
                        ><div class="grid-content grid-content-2" v-throttle @click="renderMenu('6')">
                            <div class="itemTit">应急管理系统</div>
                            <img class="iconImg" src="../../assets/menu/icon5.png" /><img class="bgImg" src="../../assets/menu/card5.png" /></div
                    ></el-col>
                    <el-col :span="6"
                        ><div class="grid-content grid-content-2" v-throttle @click="renderMenu('7')">
                            <div class="itemTit">目标责任管理系统</div>
                            <img class="iconImg" src="../../assets/menu/icon2.png" /><img class="bgImg" src="../../assets/menu/card2.png" /></div
                    ></el-col>
                    <el-col :span="6"
                        ><div class="grid-content grid-content-2" v-throttle @click="renderMenu('8')">
                            <div class="itemTit">事故管理系统</div>
                            <img class="iconImg" src="../../assets/menu/icon6.png" /><img class="bgImg" src="../../assets/menu/card6.png" /></div
                    ></el-col>
                    <el-col :span="6"
                        ><div class="grid-content grid-content-2" v-throttle @click="renderMenu('9')">
                            <div class="itemTit">设备综合管控系统</div>
                            <img class="iconImg" src="../../assets/menu/icon9.png" /><img class="bgImg" src="../../assets/menu/card9.png" /></div
                    ></el-col>
                </el-row>
                <el-row :gutter="20">
                    <el-col :span="6"
                        ><div class="grid-content grid-content-3" v-throttle @click="renderMenu('10')">
                            <div class="itemTit">安全知识图谱系统</div>
                            <img class="iconImg" src="../../assets/menu/icon3.png" /><img class="bgImg" src="../../assets/menu/card3.png" /></div
                    ></el-col>
                    <el-col :span="9"
                        ><div class="grid-content grid-content-3" v-throttle @click="renderMenu('11')">
                            <div class="itemTit">危险化学品全生命周期安全<br />管理系统</div>
                            <img class="iconImg" src="../../assets/menu/icon10.png" /><img class="bgImg" src="../../assets/menu/card10.png" /></div
                    ></el-col>
                    <el-col :span="9"
                        ><div class="grid-content grid-content-3" v-throttle @click="renderMenu('1')">
                            <div class="itemTit">基础数据权限管理系统</div>
                            <img class="iconImg" src="../../assets/menu/icon11.png" /><img class="bgImg" src="../../assets/menu/card11.png" /></div
                    ></el-col>
                </el-row>
            </div>
        </div>
    </div>
</template>
<script lang="ts">
@@ -57,220 +95,281 @@
import logoMini from '/@/assets/logo-mini.svg';
import loginIconTwo from '/@/assets/login-icon-two.svg';
import { NextLoading } from '/@/utils/loading';
import {Session} from "/@/utils/storage";
import {useRoute, useRouter} from "vue-router";
import {initBackEndControlRoutes} from "/@/router/backEnd";
import {useUserInfo} from "/@/stores/userInfo";
import { Session } from '/@/utils/storage';
import { useRoute, useRouter } from 'vue-router';
import { initBackEndControlRoutes } from '/@/router/backEnd';
import { useUserInfo } from '/@/stores/userInfo';
import { useRoutesList } from '/@/stores/routesList';
import pinia from '/@/stores';
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
import { ElMessage } from 'element-plus';
import { ElMessageBox } from 'element-plus/es';
import { useLoginApi } from '/@/api/login';
import { useI18n } from 'vue-i18n';
// 定义接口来定义对象的类型
interface LoginState {
    tabsActiveName: string;
    isScan: boolean;
    tabsActiveName: string;
    isScan: boolean;
}
export default defineComponent({
    name: 'loginIndex',
    components: {},
    setup() {
        const route = useRoute();
        const router = useRouter();
        const userInfo = useUserInfo()
        const { userInfos } = storeToRefs(userInfo);
        const storesThemeConfig = useThemeConfig();
        const { themeConfig } = storeToRefs(storesThemeConfig);
        const state = reactive<LoginState>({
            tabsActiveName: 'account',
            isScan: false,
        });
        // 获取布局配置信息
        const getThemeConfig = computed(() => {
            return themeConfig.value;
        });
        // 下拉菜单点击时
        const onLoginOut = () => {
            console.log('退出登录')
        };
        const renderMenu = async (value: string) => {
            Session.set('projectId',value)
            userInfos.value.projectId = value
            await initBackEndControlRoutes();
            router.push('/home')
        };
        // //点击进入特殊作业
        // const toSpecialWorkSys = () => {
        //     router.push('/layoutPage');
        // };
        // 页面加载时
        onMounted(() => {
            NextLoading.done();
            // loginBg();
            // loginApp()
        });
        return {
            logoMini,
            loginIconTwo,
            getThemeConfig,
            renderMenu,
            ...toRefs(state),
        };
    },
    name: 'loginIndex',
    components: {},
    setup() {
        const { t } = useI18n();
        const route = useRoute();
        const router = useRouter();
        const userInfo = useUserInfo();
        const { userInfos } = storeToRefs(userInfo);
        const routeToStore = useRoutesList(pinia);
        const { routesList } = storeToRefs(routeToStore);
        const storesThemeConfig = useThemeConfig();
        const { themeConfig } = storeToRefs(storesThemeConfig);
        const state = reactive<LoginState>({
            tabsActiveName: 'account',
            isScan: false
        });
        // 获取布局配置信息
        const getThemeConfig = computed(() => {
            return themeConfig.value;
        });
        // 下拉菜单点击时
        const onLoginOut = () => {
            ElMessageBox({
                closeOnClickModal: false,
                closeOnPressEscape: false,
                title: t('message.user.logOutTitle'),
                message: t('message.user.logOutMessage'),
                showCancelButton: true,
                confirmButtonText: t('message.user.logOutConfirm'),
                cancelButtonText: t('message.user.logOutCancel'),
                buttonSize: 'default',
                beforeClose: (action, instance, done) => {
                    if (action === 'confirm') {
                        instance.confirmButtonLoading = true;
                        instance.confirmButtonText = t('message.user.logOutExit');
                        setTimeout(() => {
                            done();
                            setTimeout(() => {
                                instance.confirmButtonLoading = false;
                            }, 300);
                        }, 700);
                    } else {
                        done();
                    }
                }
            })
                .then(async () => {
                    let res = await useLoginApi().signOut();
                    if (res.data.code === '200') {
                        Session.clear(); // 清除缓存/token等
                        // 使用 reload 时,不需要调用 resetRoute() 重置路由
                        window.location.reload();
                    } else {
                        ElMessage({
                            type: 'warning',
                            message: res.data.msg
                        });
                    }
                })
                .catch(() => {});
        };
        const renderMenu = async (value: string) => {
            Session.set('projectId', value);
            userInfos.value.projectId = value;
            await initBackEndControlRoutes().then(() => {
                let linkToMenu = [...routesList.value];
                if (linkToMenu && linkToMenu.length > 1) {
                    router.push(linkToFirstMenu(JSON.parse(JSON.stringify(linkToMenu))[1]));
                } else {
                    ElMessage({ type: 'warning', message: '你没有该项目的权限' });
                }
            });
        };
        const linkToFirstMenu: any = (value: any) => {
            let returnMenu = value;
            if (returnMenu.children?.length > 0) {
                return linkToFirstMenu(returnMenu.children[0]);
            } else {
                return returnMenu.path;
            }
        };
        // //点击进入特殊作业
        // const toSpecialWorkSys = () => {
        //     router.push('/layoutPage');
        // };
        // 页面加载时
        onMounted(() => {
            NextLoading.done();
            // loginBg();
            // loginApp()
        });
        return {
            logoMini,
            onLoginOut,
            loginIconTwo,
            getThemeConfig,
            renderMenu,
            ...toRefs(state)
        };
    }
});
</script>
<style scoped lang="scss">
.login-container {
    width: 100%;
    height: 100%;
    position: relative;
    background: url("../../assets/menu/bg_home1.jpg") no-repeat center;
    .topPanel{
        position: absolute;
        width: 100%;
        top: -100px;
        left: 0;
        height:100px;
        background: #fff;
        display: flex;
        justify-content: center;
        box-shadow: 0 8px 32px rgba(0,0,0,.1);
        animation: showDown .6s 1 ease forwards;
    width: 100%;
    height: 100%;
    position: relative;
    background: url('../../assets/menu/bg_home1.jpg') no-repeat center;
    .topPanel {
        position: absolute;
        width: 100%;
        top: -100px;
        left: 0;
        height: 100px;
        background: #fff;
        display: flex;
        justify-content: center;
        box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
        animation: showDown 0.6s 1 ease forwards;
        @keyframes showDown {
            100%{
                position: absolute;
                top: 0;
            }
        }
        .topPanelCont{
            width: 1200px;
            height: 100%;
            display: flex;
            align-items: center;
            justify-content: space-between;
        @keyframes showDown {
            100% {
                position: absolute;
                top: 0;
            }
        }
        .topPanelCont {
            width: 1200px;
            height: 100%;
            display: flex;
            align-items: center;
            justify-content: space-between;
            .topTit{
                font-size: 24px;
                font-weight: bold;
                display: flex;
                align-items: center;
                color: #333;
                line-height: 28px;
            .topTit {
                font-size: 24px;
                font-weight: bold;
                display: flex;
                align-items: center;
                color: #333;
                line-height: 28px;
                &>div:last-of-type{
                    color: #006DF5;
                }
                & > div:last-of-type {
                    color: #006df5;
                }
                span{
                    width: 1px;
                    height: 28px;
                    background: #999;
                    margin: 0 15px;
                }
            }
                span {
                    width: 1px;
                    height: 28px;
                    background: #999;
                    margin: 0 15px;
                }
            }
            .userInfo{
                display: flex;
                align-items: center;
            .userInfo {
                display: flex;
                align-items: center;
                .avator{
                    display: flex;
                    justify-content: right;
                .avator {
                    display: flex;
                    justify-content: right;
                    img{
                        width: 20px;
                        height: 20px;
                        border-radius: 10px;
                    }
                    div{
                        font-size: 15px;
                        color: #333333;
                        line-height: 20px;
                        margin-left: 6px;
                    }
                }
                span{
                    width: 1px;
                    height: 20px;
                    background: #999;
                    margin: 0 15px;
                }
                .loginOut{
                    font-size: 15px;
                    color: #333;
                    line-height: 20px;
                    cursor: pointer;
                    img {
                        width: 20px;
                        height: 20px;
                        border-radius: 10px;
                    }
                    div {
                        font-size: 15px;
                        color: #333333;
                        line-height: 20px;
                        margin-left: 6px;
                    }
                }
                span {
                    width: 1px;
                    height: 20px;
                    background: #999;
                    margin: 0 15px;
                }
                .loginOut {
                    font-size: 15px;
                    color: #333;
                    line-height: 20px;
                    cursor: pointer;
                    &:hover{
                        color: #006DF5;
                    }
                }
            }
        }
    }
                    &:hover {
                        color: #006df5;
                    }
                }
            }
        }
    }
    .menuGrid{
        width: 100%;
        position: absolute;
        top: 150px;
        display: flex;
        justify-content: center;
        .gridCont{
            width: 1200px;
            .el-row {
                margin-bottom: 20px;
            }
            .el-row:last-child {
                margin-bottom: 0;
            }
            .el-col {
                border-radius: 8px;
            }
    .menuGrid {
        width: 100%;
        position: absolute;
        top: 150px;
        display: flex;
        justify-content: center;
        .gridCont {
            width: 1200px;
            .el-row {
                margin-bottom: 20px;
            }
            .el-row:last-child {
                margin-bottom: 0;
            }
            .el-col {
                border-radius: 8px;
            }
            .grid-content {
                border-radius: 10px;
                height: 234px;
                padding: 32px;
                position: relative;
                background-image: linear-gradient(135deg,#00C0F5,#44b1ff);
                overflow: hidden;
                cursor: pointer;
                transition: .3s;
                border: none;
            .grid-content {
                border-radius: 10px;
                height: 234px;
                padding: 32px;
                position: relative;
                background-image: linear-gradient(135deg, #00c0f5, #44b1ff);
                overflow: hidden;
                cursor: pointer;
                transition: 0.3s;
                border: none;
                &:hover{
                    box-shadow: 0 8px 32px rgba(20,97,234,.4);
                }
                &:hover {
                    box-shadow: 0 8px 32px rgba(20, 97, 234, 0.4);
                }
                .itemTit{
                    font-size: 24px;
                    line-height: 36px;
                    height: 40%;
                    font-family: "PingFang SC";
                    font-weight: lighter;
                    color: #fff;
                    margin-bottom: 25px;
                }
                .iconImg{
                    width: 80px;
                    height: 80px;
                }
                .itemTit {
                    font-size: 24px;
                    line-height: 36px;
                    height: 40%;
                    font-family: 'PingFang SC';
                    font-weight: lighter;
                    color: #fff;
                    margin-bottom: 25px;
                }
                .iconImg {
                    width: 80px;
                    height: 80px;
                }
                .bgImg{
                    position: absolute;
                    right: 0;
                    bottom: 0;
                }
            }
                .bgImg {
                    position: absolute;
                    right: 0;
                    bottom: 0;
                }
            }
            /*.grid-content-2{*/
            /*    background-image: linear-gradient(135deg,#0098F5,#1461EA);*/
            /*}*/
            /*.grid-content-2{*/
            /*    background-image: linear-gradient(135deg,#0098F5,#1461EA);*/
            /*}*/
            /*.grid-content-3{*/
            /*    background-image: linear-gradient(135deg,#006DF5,#1450EA);*/
            /*}*/
        }
    }
            /*.grid-content-3{*/
            /*    background-image: linear-gradient(135deg,#006DF5,#1450EA);*/
            /*}*/
        }
    }
}
</style>
src/views/loginPage/component/accountLogin.vue
@@ -14,6 +14,7 @@
                v-model="ruleForm.password"
                autocomplete="off"
                size="large"
                @keyup.enter.native="onSignIn"
            >
                <template #prefix>
                    <el-icon class="el-input__icon" style="margin-right: 20px"><img src="../../../assets/loginPage/login_icon_password.png" style="width: 24px;height: 24px"></el-icon>
src/views/system/department/component/deptDialog.vue
@@ -7,23 +7,23 @@
                        <el-form-item label="上级部门">
                            <el-cascader
                                :options="deptData"
                                :props="{ checkStrictly: true, value: 'id', label: 'name' }"
                                :props="{ emitPath: false, checkStrictly: true, value: 'depId', label: 'depName' }"
                                placeholder="请选择部门"
                                clearable
                                class="w100"
                                v-model="departmentForm.struct"
                                v-model="departmentForm.parentDepId"
                            >
                            </el-cascader>
                        </el-form-item>
                    </el-col>
                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
                        <el-form-item label="部门名称">
                            <el-input v-model="departmentForm.name" placeholder="请输入部门名称" clearable></el-input>
                            <el-input v-model="departmentForm.depName" placeholder="请输入部门名称" clearable></el-input>
                        </el-form-item>
                    </el-col>
                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
                        <el-form-item label="部门描述">
                            <el-input v-model="departmentForm.info" type="textarea" placeholder="请输入部门描述" maxlength="150"></el-input>
                            <el-input v-model="departmentForm.depInfo" type="textarea" placeholder="请输入部门描述" maxlength="150"></el-input>
                        </el-form-item>
                    </el-col>
                </el-row>
@@ -31,7 +31,7 @@
            <template #footer>
                <span class="dialog-footer">
                    <el-button @click="onCancel" size="default">取 消</el-button>
                    <el-button type="primary" @click="onSubmit" size="default">新 增</el-button>
                    <el-button type="primary" @click="onSubmit" size="default">确 定</el-button>
                </span>
            </template>
        </el-dialog>
@@ -45,7 +45,6 @@
// 定义接口来定义对象的类型
interface TableDataRow {
    struct: Array<string>;
    name:string,
    info:string,
    parentId:string,
@@ -55,10 +54,9 @@
    title:string;
    isShowDialog: boolean;
    departmentForm: {
        struct: Array<string>;
        name:string,
        info:string,
        parentId:string
        depName:string,
        depInfo:string,
        parentDepId:string
    };
    deptData: Array<TableDataRow>;
}
@@ -70,10 +68,9 @@
            title:'',
            isShowDialog: false,
            departmentForm: {
                name:'',
                parentId:'',
                info:'',
                struct:[]
                depName:'',
                parentDepId:'',
                depInfo:'',
            },
            deptData: [], // 部门数据
        });
@@ -84,10 +81,9 @@
            if(type === '新增'){
                state.title = '新增部门'
                state.departmentForm = {
                    name:'',
                    parentId:'',
                    info:'',
                    struct:[]
                    depName:'',
                    parentDepId:'',
                    depInfo:'',
                }
            }else{
                state.title = '修改部门'
@@ -104,10 +100,6 @@
        };
        // 新增
        const onSubmit = async () => {
            if(state.departmentForm.struct && state.departmentForm.struct !== []){
                let departmentId = JSON.parse(JSON.stringify(state.departmentForm.struct))
                state.departmentForm.parentId = departmentId[departmentId.length - 1]
            }
            if(state.title === '新增部门'){
                let res = await departmentApi().addDepartment(state.departmentForm)
                if(res.data.code === '200'){
src/views/system/department/index.vue
@@ -19,11 +19,11 @@
            <el-table
                :data="tableData.data"
                style="width: 100%"
                row-key="id"
                row-key="depId"
                :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
            >
                <el-table-column prop="name" label="部门名称" show-overflow-tooltip> </el-table-column>
                <el-table-column prop="info" label="部门描述" show-overflow-tooltip></el-table-column>
                <el-table-column prop="depName" label="部门名称" show-overflow-tooltip> </el-table-column>
                <el-table-column prop="depInfo" label="部门描述" show-overflow-tooltip></el-table-column>
                <el-table-column label="操作" show-overflow-tooltip width="140">
                    <template #default="scope">
                        <el-button size="small" text type="primary" @click="onOpenDeptDialog('新增','')">新增</el-button>
@@ -42,6 +42,7 @@
import { ElMessageBox, ElMessage } from 'element-plus';
import deptDialog from '/@/views/system/department/component/deptDialog.vue';
import {departmentApi} from "/@/api/department";
import {useRoleApi} from "/@/api/role";
// 定义接口来定义对象的类型
interface TableDataRow {
@@ -49,7 +50,7 @@
    status: boolean;
    parentId: number;
    info: string;
    id: number;
    depId: number;
    children?: TableDataRow[];
}
interface TableDataState {
@@ -91,13 +92,26 @@
        };
        // 删除当前行
        const onTabelRowDel = (row: TableDataRow) => {
            ElMessageBox.confirm(`此操作将永久删除部门:${row.id}, 是否继续?`, '提示', {
            ElMessageBox.confirm(`此操作将永久删除部门:${row.depId}, 是否继续?`, '提示', {
                confirmButtonText: '删除',
                cancelButtonText: '取消',
                type: 'warning',
            }).then(() => {
                    ElMessage.success('删除成功');
            }).catch(() => {});
            }).then(async () => {
                let res = await departmentApi().deleteDepartment({depId:row.depId})
                if(res.data.code ==='200'){
                    ElMessage({
                        type:'success',
                        duration:2000,
                        message:'删除成功'
                    })
                    await initTableData()
                }else{
                    ElMessage({
                        type:'warning',
                        message:res.data.msg
                    })
                }
            }).catch(() => {});
        };
        // 页面加载时
        onMounted(() => {
src/views/system/menu/component/menuDialog.vue
@@ -38,7 +38,7 @@
                    </el-col>
                    <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
                        <el-form-item label="菜单图标">
                            <input placeholder="请输入菜单图标" v-model="ruleForm.meta.icon" type="all" />
                            <el-input placeholder="请输入菜单图标" v-model="ruleForm.meta.icon" type="all" />
                            <!--                            <IconSelector placeholder="请输入菜单图标" v-model="ruleForm.meta.icon" type="all" />-->
                        </el-form-item>
                    </el-col>
src/views/system/role/component/roleDialog.vue
@@ -5,17 +5,17 @@
                <el-row :gutter="35">
                    <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
                        <el-form-item label="角色名称">
                            <el-input v-model="roleForm.name" placeholder="请输入角色名称" clearable></el-input>
                            <el-input v-model="roleForm.roleName" placeholder="请输入角色名称" clearable></el-input>
                        </el-form-item>
                    </el-col>
                    <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
                        <el-form-item label="角色标识">
                            <el-input v-model="roleForm.code" placeholder="请输入角色标识" clearable></el-input>
                            <el-input v-model="roleForm.roleCode" placeholder="请输入角色标识" clearable></el-input>
                        </el-form-item>
                    </el-col>
                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
                        <el-form-item label="角色描述">
                            <el-input v-model="roleForm.info" type="textarea" placeholder="请输入角色描述" maxlength="150"></el-input>
                            <el-input v-model="roleForm.roleInfo" type="textarea" placeholder="请输入角色描述" maxlength="150"></el-input>
                        </el-form-item>
                    </el-col>
                </el-row>
@@ -33,7 +33,6 @@
<script lang="ts">
import { ElMessage } from 'element-plus';
import { reactive, toRefs, defineComponent } from 'vue';
import {departmentApi} from "/@/api/department";
import {useRoleApi} from "/@/api/role";
// 定义接口来定义对象的类型
@@ -47,9 +46,9 @@
    buttonName:string,
    isShowRoleDialog: boolean;
    roleForm: {
        name: string;
        code: string;
        info: string;
        roleName: string;
        roleCode: string;
        roleInfo: string;
    };
    menuData: Array<MenuDataTree>;
    menuProps: {
@@ -66,9 +65,9 @@
            title:'',
            buttonName:'',
            roleForm: {
                name: '', // 角色名称
                code: '', // 角色标识
                info: '', // 排序
                roleName: '', // 角色名称
                roleCode: '', // 角色标识
                roleInfo: '', // 排序
            },
            menuData: [],
            menuProps: {
@@ -83,9 +82,9 @@
                state.title = '新增角色'
                state.buttonName = '新增'
                state.roleForm = {
                    name:'',
                    code:'',
                    info:'',
                    roleName:'',
                    roleCode:'',
                    roleInfo:'',
                }
            }else{
                state.title = '修改角色'
src/views/system/role/index.vue
@@ -18,15 +18,15 @@
            </div>
            <el-table :data="tableData.data" style="width: 100%">
                <el-table-column type="index" label="序号" width="60" />
                <el-table-column prop="name" label="角色名称" show-overflow-tooltip></el-table-column>
                <el-table-column prop="code" label="角色标识" show-overflow-tooltip></el-table-column>
                <el-table-column prop="info" label="角色描述" show-overflow-tooltip></el-table-column>
                <el-table-column prop="roleName" label="角色名称" show-overflow-tooltip></el-table-column>
                <el-table-column prop="roleCode" label="角色标识" show-overflow-tooltip></el-table-column>
                <el-table-column prop="roleInfo" label="角色描述" show-overflow-tooltip></el-table-column>
                <el-table-column prop="createTime" label="创建时间" show-overflow-tooltip></el-table-column>
                <el-table-column label="操作" width="150">
                    <template #default="scope">
                        <el-button size="small" text type="primary" @click="onOpenDialogRef('新增','')">新增</el-button>
                        <el-button size="small" text type="primary" @click="onOpenDialogRef('修改',scope.row)">修改</el-button>
                        <el-button size="small" text type="primary" @click="onTabelRowDel(scope.row)">删除</el-button>
                        <el-button size="small" text type="primary" @click="onRowDel(scope.row)">删除</el-button>
                    </template>
                </el-table-column>
            </el-table>
@@ -40,10 +40,13 @@
import { ElMessageBox, ElMessage } from 'element-plus';
import roleDialog from '/@/views/system/role/component/roleDialog.vue';
import {useRoleApi} from "/@/api/role";
import {useMenuApi} from "/@/api/menu";
import {Session} from "/@/utils/storage";
import {initBackEndControlRoutes} from "/@/router/backEnd";
// 定义接口来定义对象的类型
interface TableData {
    roleName: string;
    roleName: string;
    roleSign: string;
    describe: string;
    sort: number;
@@ -100,9 +103,22 @@
                confirmButtonText: '确认',
                cancelButtonText: '取消',
                type: 'warning',
            }).then(() => {
                    ElMessage.success('删除成功');
            }).catch(() => {});
            }).then(async () => {
                let res = await useRoleApi().deleteRole({roleId:row.roleId})
                if(res.data.code ==='200'){
                    ElMessage({
                        type:'success',
                        duration:2000,
                        message:'删除成功'
                    })
                    await initRoleTableData()
                }else{
                    ElMessage({
                        type:'warning',
                        message:res.data.msg
                    })
                }
            }).catch(() => {});
        };
        const handleSearch = () => {
            initRoleTableData()