This commit is contained in:
huzhushan 2021-04-19 18:50:34 +08:00
parent 33fda82dd9
commit bbf03e66fb
53 changed files with 719 additions and 726 deletions

View File

@ -1,22 +1,52 @@
module.exports = {
root: true,
env: {
browser: true,
node: true,
es6: true,
},
extends: ["plugin:vue/vue3-essential", "eslint:recommended", "@vue/prettier"],
parserOptions: {
"parser": "babel-eslint"
"parser": "babel-eslint",
"sourceType": 'module',
"ecmaVersion": 2020,
},
rules: {
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
"no-console": 0,
'no-use-before-define': 'off',
'no-unused-vars': [
'warn',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
},
],
'no-prototype-builtins': 'off',
'space-before-function-paren': 'off',
'vue/custom-event-name-casing': 'off',
'vue/attributes-order': 'off',
'vue/one-component-per-file': 'off',
'vue/html-closing-bracket-newline': 'off',
'vue/max-attributes-per-line': 'off',
'vue/multiline-html-element-content-newline': 'off',
'vue/singleline-html-element-content-newline': 'off',
'vue/attribute-hyphenation': 'off',
'vue/require-default-prop': 'off',
'vue/no-unused-components': 'warn',
'vue/no-setup-props-destructure': 'off',
"prettier/prettier": [
"warn",
{
// singleQuote: none,
// semi: false,
trailingComma: "es5",
},
],
'printWidth': 80, // 每行代码长度默认80
'tabWidth': 2, // 每个tab相当于多少个空格默认2
'useTabs': false, // 是否使用tab进行缩进默认false
'singleQuote': true, // 使用单引号默认false
'semi': true, // 声明结尾使用分号(默认true)
'trailingComma': 'es5', // 多行使用拖尾逗号默认none
'bracketSpacing': true, // 对象字面量的大括号间使用空格默认true
'jsxBracketSameLine': false, // 多行JSX中的>放置在最后一行的结尾而不是另起一行默认false
'arrowParens': 'avoid', // 只有一个参数的箭头函数的参数是否带括号默认avoid
}
]
},
};

View File

@ -13,7 +13,7 @@
"build": "vite build",
"build:mock": "vite build --mode mock",
"serve": "vite preview",
"lint": "eslint --ext .js,.vue src"
"lint": "eslint --fix --ext .js,.vue src"
},
"browserslist": [
"> 1%",

View File

@ -1,11 +0,0 @@
module.exports = {
printWidth: 80, // 每行代码长度默认80
tabWidth: 2, // 每个tab相当于多少个空格默认2
useTabs: false, // 是否使用tab进行缩进默认false
singleQuote: false, // 使用单引号默认false
semi: true, // 声明结尾使用分号(默认true)
trailingComma: 'es5', // 多行使用拖尾逗号默认none
bracketSpacing: true, // 对象字面量的大括号间使用空格默认true
jsxBracketSameLine: false, // 多行JSX中的>放置在最后一行的结尾而不是另起一行默认false
arrowParens: "avoid", // 只有一个参数的箭头函数的参数是否带圆括号默认avoid
};

View File

@ -1,10 +1,10 @@
import request from '@/utils/request'
import request from '@/utils/request';
// 登录接口
export const Login = data => {
return request({
url: "/api/login",
method: "post",
url: '/api/login',
method: 'post',
data,
});
};
@ -12,7 +12,7 @@ export const Login = data => {
// 获取登录用户信息
export const GetUserinfo = () => {
return request({
url: "/api/userinfo",
method: "get"
url: '/api/userinfo',
method: 'get',
});
};

View File

@ -1,10 +1,9 @@
import request from '@/utils/request'
import request from '@/utils/request';
// 获取菜单
export const GetMenus = () => {
return request({
url: "/api/menus",
method: "get"
url: '/api/menus',
method: 'get',
});
};

View File

@ -212,42 +212,42 @@
</div>
</template>
<script>
import { defineComponent, reactive, toRefs, onBeforeMount } from "vue";
import { defineComponent, reactive, toRefs, onBeforeMount } from 'vue';
const formatDate = (date, format) => {
var obj = {
"M+": date.getMonth() + 1,
"D+": date.getDate(),
"H+": date.getHours(),
"m+": date.getMinutes(),
"s+": date.getSeconds(),
"q+": Math.floor((date.getMonth() + 3) / 3),
"S+": date.getMilliseconds(),
'M+': date.getMonth() + 1,
'D+': date.getDate(),
'H+': date.getHours(),
'm+': date.getMinutes(),
's+': date.getSeconds(),
'q+': Math.floor((date.getMonth() + 3) / 3),
'S+': date.getMilliseconds(),
};
if (/(y+)/i.test(format)) {
format = format.replace(
RegExp.$1,
(date.getFullYear() + "").substr(4 - RegExp.$1.length)
(date.getFullYear() + '').substr(4 - RegExp.$1.length)
);
}
for (var k in obj) {
if (new RegExp("(" + k + ")").test(format)) {
if (new RegExp('(' + k + ')').test(format)) {
format = format.replace(
RegExp.$1,
RegExp.$1.length == 1
? obj[k]
: ("00" + obj[k]).substr(("" + obj[k]).length)
: ('00' + obj[k]).substr(('' + obj[k]).length)
);
}
}
return format;
};
const getSearchModel = (search) => {
const getSearchModel = search => {
const searchModel = {};
if (search && search.fields) {
search.fields.forEach((item) => {
search.fields.forEach(item => {
switch (item.type) {
case "checkbox":
case "checkbox-button":
case 'checkbox':
case 'checkbox-button':
searchModel[item.name] = [];
break;
default:
@ -257,7 +257,7 @@ const getSearchModel = (search) => {
searchModel[item.name] = item.defaultValue;
//
if (
(item.type === "daterange" || item.type === "datetimerange") &&
(item.type === 'daterange' || item.type === 'datetimerange') &&
!!item.trueNames &&
Array.isArray(item.defaultValue)
) {
@ -279,7 +279,7 @@ export default defineComponent({
//
title: {
type: String,
default: "",
default: '',
},
//
hideTitleBar: {
@ -298,14 +298,12 @@ export default defineComponent({
//
columns: {
type: Array,
default: function (params) {
return [];
},
default: () => [],
},
// KeyelementUItablerow-key
rowKey: {
type: String,
default: "id",
default: 'id',
},
// false
pagination: {
@ -317,18 +315,18 @@ export default defineComponent({
//
// 1transformtransform
// 2name
const optimizeFields = (search) => {
const optimizeFields = search => {
const searchModel = JSON.parse(JSON.stringify(state.searchModel));
if (search && search.fields) {
search.fields.forEach((item) => {
search.fields.forEach(item => {
if (!searchModel.hasOwnProperty(item.name)) {
return;
}
if (!!item.transform) {
if (item.transform) {
searchModel[item.name] = item.transform(searchModel[item.name]);
}
if (
(item.type === "daterange" || item.type === "datetimerange") &&
(item.type === 'daterange' || item.type === 'datetimerange') &&
!!item.trueNames
) {
delete searchModel[item.name];
@ -373,7 +371,7 @@ export default defineComponent({
},
//
handleReset() {
if (JSON.stringify(state.searchModel) === "{}") {
if (JSON.stringify(state.searchModel) === '{}') {
return;
}
state.pageNum = 1;
@ -386,52 +384,52 @@ export default defineComponent({
},
//
handleCurrentChange(page) {
handleCurrentChange() {
getTableData();
},
// size
handleSizeChange(value) {
handleSizeChange() {
state.pageNum = 1;
getTableData();
},
//
handleSelectionChange(arr) {
emit("selectionChange", arr);
emit('selectionChange', arr);
},
//
filterHandler(value, row, column) {
const property = column["property"];
const property = column['property'];
return row[property] === value;
},
//
handleDateChange(date, item, format) {
state.searchModel[item.name] = !!date ? formatDate(date, format) : "";
state.searchModel[item.name] = date ? formatDate(date, format) : '';
},
handleRangeChange(date, item, format) {
const arr = !!date && date.map((d) => formatDate(d, format));
state.searchModel[item.name] = !!arr ? arr : [];
const arr = !!date && date.map(d => formatDate(d, format));
state.searchModel[item.name] = arr ? arr : [];
if (!item.trueNames) {
return;
}
if (!!arr) {
if (arr) {
arr.forEach((val, index) => {
state.searchModel[item.trueNames[index]] = val;
});
} else {
item.trueNames.forEach((key) => {
item.trueNames.forEach(key => {
delete state.searchModel[key];
});
}
},
});
if (typeof props.pagination === "object") {
if (typeof props.pagination === 'object') {
const { layout, pageSizes, style } = props.pagination;
state.paginationConfig = {
show: true,
layout: layout || "total, sizes, prev, pager, next, jumper",
layout: layout || 'total, sizes, prev, pager, next, jumper',
pageSizes: pageSizes || [10, 20, 30, 40, 50, 100],
style: style || {},
};
@ -491,4 +489,3 @@ export default defineComponent({
}
}
</style>

View File

@ -1,21 +1,18 @@
<template>
<svg
class="icon"
aria-hidden="true"
>
<svg class="icon" aria-hidden="true">
<use :xlink:href="symbolId" />
</svg>
</template>
<script>
import { defineComponent, computed } from "vue";
import { defineComponent, computed } from 'vue';
export default defineComponent({
name: "SvgIcon",
name: 'SvgIcon',
props: {
prefix: {
type: String,
default: "icon",
default: 'icon',
},
name: {
type: String,

View File

@ -1,2 +1,2 @@
export { default as SvgIcon } from "@/components/SvgIcon/index.vue";
export { default as ProTable } from "@/components/ProTable/index.vue";
export { default as SvgIcon } from '@/components/SvgIcon/index.vue';
export { default as ProTable } from '@/components/ProTable/index.vue';

View File

@ -6,9 +6,9 @@
</router-view>
</template>
<script>
import { computed, defineComponent } from "vue";
import { useRoute } from "vue-router";
import { useStore } from "vuex";
import { computed, defineComponent } from 'vue';
import { useRoute } from 'vue-router';
import { useStore } from 'vuex';
export default defineComponent({
setup() {

View File

@ -1,23 +1,16 @@
<template>
<i
v-if="isElementIcon"
:class="`icon ${icon}`"
/>
<svg-icon
class="icon"
v-else-if="!!icon"
:name="icon"
/>
<i v-if="isElementIcon" :class="`icon ${icon}`" />
<svg-icon class="icon" v-else-if="!!icon" :name="icon" />
<span>{{ title }}</span>
</template>
<script>
import { computed, defineComponent } from "vue";
import { computed, defineComponent } from 'vue';
export default defineComponent({
props: ["title", "icon"],
props: ['title', 'icon'],
setup({ icon }) {
const isElementIcon = computed(() => icon && icon.startsWith("el-icon"));
const isElementIcon = computed(() => icon && icon.startsWith('el-icon'));
return {
isElementIcon,

View File

@ -1,22 +1,18 @@
<template>
<div class="brand">
<img
class="logo"
src="~@/assets/logo.svg"
@click="goHome"
>
<img class="logo" src="~@/assets/logo.svg" @click="goHome" />
<div class="title">Vue3 Element Admin</div>
</div>
</template>
<script>
import { defineComponent } from "vue";
import { useRouter } from "vue-router";
import { defineComponent } from 'vue';
import { useRouter } from 'vue-router';
export default defineComponent({
setup() {
const router = useRouter();
const goHome = () => {
router.push("/");
router.push('/');
};
return { goHome };
},
@ -44,4 +40,3 @@ export default defineComponent({
}
}
</style>

View File

@ -15,11 +15,11 @@
</el-scrollbar>
</template>
<script>
import { computed, defineComponent } from "vue";
import Submenu from "./Submenu.vue";
import { useStore } from "vuex";
import { useRoute } from "vue-router";
import config from "./config/menu.module.scss";
import { computed, defineComponent } from 'vue';
import Submenu from './Submenu.vue';
import { useStore } from 'vuex';
import { useRoute } from 'vue-router';
import config from './config/menu.module.scss';
export default defineComponent({
components: {
@ -35,7 +35,7 @@ export default defineComponent({
const route = useRoute();
const store = useStore();
store.dispatch(
"menu/generateMenus",
'menu/generateMenus',
store.state.account.userinfo && store.state.account.userinfo.role
);
@ -80,7 +80,7 @@ export default defineComponent({
background-color: $collapseMenuActiveBg !important;
color: $collapseMenuActiveColor !important;
&::before {
content: "";
content: '';
position: absolute;
left: 0;
top: 0;

View File

@ -1,23 +1,10 @@
<template>
<el-menu-item
v-if="!menu.children"
:index="menu.url"
>
<item
:icon="menu.icon"
:title="menu.title"
/>
<el-menu-item v-if="!menu.children" :index="menu.url">
<item :icon="menu.icon" :title="menu.title" />
</el-menu-item>
<el-submenu
v-else
:index="menu.url"
>
<el-submenu v-else :index="menu.url">
<template #title>
<item
:icon="menu.icon"
:title="menu.title"
/>
<item :icon="menu.icon" :title="menu.title" />
</template>
<submenu
v-for="submenu in menu.children"
@ -28,10 +15,10 @@
</el-submenu>
</template>
<script>
import { defineComponent } from "vue";
import Item from "./Item.vue";
import { defineComponent } from 'vue';
import Item from './Item.vue';
export default defineComponent({
name: "Submenu",
name: 'Submenu',
components: {
Item,
},

View File

@ -10,10 +10,10 @@
</template>
<script>
import { defineComponent, computed } from "vue";
import Logo from "./Logo.vue";
import Menus from "./Menus.vue";
import { useStore } from "vuex";
import { defineComponent, computed } from 'vue';
import Logo from './Logo.vue';
import Menus from './Menus.vue';
import { useStore } from 'vuex';
export default defineComponent({
components: {
@ -26,7 +26,7 @@ export default defineComponent({
const device = computed(() => store.state.app.device);
const closeSidebar = () => {
store.commit("app/setCollapse", 1);
store.commit('app/setCollapse', 1);
};
return {

View File

@ -1,12 +1,12 @@
import { onMounted, onBeforeUnmount, reactive, toRefs, nextTick } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { useStore } from 'vuex'
import { isAffix } from './useTags'
import { useStore } from 'vuex';
import { isAffix } from './useTags';
export const useContextMenu = (tagList) => {
const store = useStore()
const router = useRouter()
const route = useRoute()
export const useContextMenu = tagList => {
const store = useStore();
const router = useRouter();
const route = useRoute();
const state = reactive({
visible: false,
@ -23,11 +23,11 @@ export const useContextMenu = (tagList) => {
state.visible = false;
},
refreshSelectedTag(tag) {
store.dispatch("tags/delCacheList", tag)
store.dispatch('tags/delCacheList', tag);
const { fullPath } = tag;
nextTick(() => {
router.replace({
path: "/redirect" + fullPath,
path: '/redirect' + fullPath,
});
});
},
@ -35,64 +35,68 @@ export const useContextMenu = (tagList) => {
if (isAffix(tag)) return;
const closedTagIndex = tagList.value.findIndex(
(item) => item.fullPath === tag.fullPath
item => item.fullPath === tag.fullPath
);
store.dispatch("tags/delTag", tag)
store.dispatch('tags/delTag', tag);
if (isActive(tag)) {
toLastTag(closedTagIndex - 1);
}
},
closeOtherTags() {
store.dispatch("tags/delOtherTags", state.selectedTag)
store.dispatch('tags/delOtherTags', state.selectedTag);
router.push(state.selectedTag);
},
closeLeftTags() {
state.closeSomeTags('left')
state.closeSomeTags('left');
},
closeRightTags() {
state.closeSomeTags('right')
state.closeSomeTags('right');
},
closeSomeTags(direction) {
const index = tagList.value.findIndex(
(item) => item.fullPath === state.selectedTag.fullPath
item => item.fullPath === state.selectedTag.fullPath
);
if ((direction === 'left' && index <= 0) || (direction === 'right' && index >= tagList.value.length - 1)) {
if (
(direction === 'left' && index <= 0) ||
(direction === 'right' && index >= tagList.value.length - 1)
) {
return;
}
const needToClose = direction === 'left' ? tagList.value.slice(0, index) : tagList.value.slice(index + 1)
store.dispatch("tags/delSomeTags", needToClose)
const needToClose =
direction === 'left'
? tagList.value.slice(0, index)
: tagList.value.slice(index + 1);
store.dispatch('tags/delSomeTags', needToClose);
router.push(state.selectedTag);
},
closeAllTags() {
store.dispatch("tags/delAllTags")
router.push("/");
}
})
store.dispatch('tags/delAllTags');
router.push('/');
},
});
const isActive = (tag) => {
const isActive = tag => {
return tag.fullPath === route.fullPath;
}
};
const toLastTag = (lastTagIndex) => {
const toLastTag = lastTagIndex => {
const lastTag = tagList.value[lastTagIndex];
if (!!lastTag) {
if (lastTag) {
router.push(lastTag.fullPath);
} else {
router.push("/");
}
router.push('/');
}
};
onMounted(() => {
document.addEventListener("click", state.closeMenu);
document.addEventListener('click', state.closeMenu);
});
onBeforeUnmount(() => {
document.removeEventListener("click", state.closeMenu);
document.removeEventListener('click', state.closeMenu);
});
return toRefs(state)
}
return toRefs(state);
};

View File

@ -1,15 +1,14 @@
import { ref } from 'vue'
import { ref } from 'vue';
export const useScrollbar = (tagsItem) => {
export const useScrollbar = tagsItem => {
const scrollContainer = ref(null);
const handleScroll = (e) => {
const handleScroll = e => {
const eventDelta = e.wheelDelta || -e.deltaY;
scrollContainer.value.wrap.scrollLeft -= eventDelta / 4;
};
const moveToTarget = (currentTag) => {
const moveToTarget = currentTag => {
const containerWidth = scrollContainer.value.scrollbar.offsetWidth;
const scrollWrapper = scrollContainer.value.wrap;
const tagList = tagsItem.value;
@ -26,8 +25,11 @@ export const useScrollbar = (tagsItem) => {
} else if (lastTag === currentTag) {
scrollWrapper.scrollLeft = scrollWrapper.scrollWidth - containerWidth;
} else {
const el = currentTag.$el.nextElementSibling
scrollWrapper.scrollLeft = el.offsetLeft + el.offsetWidth > containerWidth ? el.offsetLeft - el.offsetWidth : 0
const el = currentTag.$el.nextElementSibling;
scrollWrapper.scrollLeft =
el.offsetLeft + el.offsetWidth > containerWidth
? el.offsetLeft - el.offsetWidth
: 0;
}
};
@ -36,4 +38,4 @@ export const useScrollbar = (tagsItem) => {
handleScroll,
moveToTarget,
};
}
};

View File

@ -1,69 +1,71 @@
import { useScrollbar } from "./useScrollbar";
import { useScrollbar } from './useScrollbar';
import { watch, computed, ref, nextTick, onBeforeMount } from 'vue';
import { useRouter } from 'vue-router';
import { useStore } from 'vuex'
import { useStore } from 'vuex';
export const isAffix = (tag) => {
export const isAffix = tag => {
return !!tag.meta && !!tag.meta.affix;
};
export const useTags = () => {
const store = useStore()
const store = useStore();
const router = useRouter();
const route = router.currentRoute;
const routes = computed(() => router.getRoutes());
const tagList = computed(() => store.state.tags.tagList);
const tagsItem = ref([])
const tagsItem = ref([]);
const setItemRef = (i, el) => {
tagsItem.value[i] = el
}
tagsItem.value[i] = el;
};
const scrollbar = useScrollbar(tagsItem);
watch(() => tagList.value.length, () => {
tagsItem.value = []
})
watch(
() => tagList.value.length,
() => {
tagsItem.value = [];
}
);
const filterAffixTags = (routes) => {
return routes.filter((route) => isAffix(route));
const filterAffixTags = routes => {
return routes.filter(route => isAffix(route));
};
const initTags = () => {
const affixTags = filterAffixTags(routes.value);
for (const tag of affixTags) {
if (!!tag.name) {
store.dispatch("tags/addTagList", tag);
if (tag.name) {
store.dispatch('tags/addTagList', tag);
}
}
};
const addTag = () => {
const tag = route.value;
if (!!tag.name && tag.matched[0].components.default.name === "layout") {
store.dispatch("tags/addTag", tag);
if (!!tag.name && tag.matched[0].components.default.name === 'layout') {
store.dispatch('tags/addTag', tag);
}
};
const saveActivePosition = (tag) => {
const saveActivePosition = tag => {
const index = tagList.value.findIndex(
(item) => item.fullPath === tag.fullPath
item => item.fullPath === tag.fullPath
);
store.dispatch("tags/saveActivePosition", Math.max(0, index));
store.dispatch('tags/saveActivePosition', Math.max(0, index));
};
const moveToCurrentTag = () => {
nextTick(() => {
for (const tag of tagsItem.value) {
if (!!tag && (tag.to.path === route.value.path)) {
if (!!tag && tag.to.path === route.value.path) {
scrollbar.moveToTarget(tag);
if (tag.to.fullPath !== route.value.fullPath) {
store.dispatch("tags/updateTagList", route.value);
store.dispatch('tags/updateTagList', route.value);
}
break;
}
@ -71,8 +73,6 @@ export const useTags = () => {
});
};
onBeforeMount(() => {
initTags();
addTag();
@ -89,6 +89,6 @@ export const useTags = () => {
tagList,
setItemRef,
isAffix,
...scrollbar
}
}
...scrollbar,
};
};

View File

@ -10,7 +10,7 @@
v-for="(tag, i) in tagList"
:key="tag.fullPath"
:to="tag"
:ref="(el) => setItemRef(i, el)"
:ref="el => setItemRef(i, el)"
custom
v-slot="{ navigate, isExactActive }"
>
@ -46,17 +46,17 @@
</template>
<script>
import { defineComponent } from "vue";
import { useTags } from "./hooks/useTags";
import { useContextMenu } from "./hooks/useContextMenu";
import { defineComponent } from 'vue';
import { useTags } from './hooks/useTags';
import { useContextMenu } from './hooks/useContextMenu';
export default defineComponent({
name: "Tagsbar",
name: 'Tagsbar',
setup() {
const tags = useTags();
const contextMenu = useContextMenu(tags.tagList);
const onScroll = (e) => {
const onScroll = e => {
tags.handleScroll(e);
contextMenu.closeMenu.value();
};

View File

@ -15,9 +15,9 @@
</el-breadcrumb>
</template>
<script>
import { defineComponent, computed, ref, onBeforeMount, watch } from "vue";
import { useRouter } from "vue-router";
import { useStore } from "vuex";
import { defineComponent, computed, ref, onBeforeMount, watch } from 'vue';
import { useRouter } from 'vue-router';
import { useStore } from 'vuex';
export default defineComponent({
setup() {
@ -27,13 +27,13 @@ export default defineComponent({
const route = router.currentRoute; // 使useRoutewatch
const breadcrumbs = ref([]);
const getBreadcrumbs = (route) => {
const home = [{ path: "/", meta: { title: "首页" } }];
if (route.name === "home") {
const getBreadcrumbs = route => {
const home = [{ path: '/', meta: { title: '首页' } }];
if (route.name === 'home') {
return home;
} else {
const matched = route.matched.filter(
(item) => !!item.meta && !!item.meta.title
item => !!item.meta && !!item.meta.title
);
return [...home, ...matched];
@ -44,7 +44,7 @@ export default defineComponent({
breadcrumbs.value = getBreadcrumbs(route.value);
});
watch(route, (newRoute) => {
watch(route, newRoute => {
breadcrumbs.value = getBreadcrumbs(newRoute);
});

View File

@ -6,15 +6,15 @@
></i>
</template>
<script>
import { defineComponent, computed } from "vue";
import { useStore } from "vuex";
import { defineComponent, computed } from 'vue';
import { useStore } from 'vuex';
export default defineComponent({
setup() {
const store = useStore();
const collapse = computed(() => !!store.state.app.sidebar.collapse);
const handleToggleMenu = () => {
store.commit("app/setCollapse", +!collapse.value);
store.commit('app/setCollapse', +!collapse.value);
};
return {
collapse,

View File

@ -21,19 +21,19 @@
</el-dropdown>
</template>
<script>
import { computed, defineComponent } from "vue";
import { useStore } from "vuex";
import { useRouter } from "vue-router";
import { computed, defineComponent } from 'vue';
import { useStore } from 'vuex';
import { useRouter } from 'vue-router';
export default defineComponent({
setup() {
const store = useStore();
const router = useRouter();
const userinfo = computed(() => store.state.account.userinfo);
const logout = () => {
store.commit("app/clearToken");
store.commit("account/clearUserinfo");
store.dispatch("tags/delAllTags");
router.push("/login");
store.commit('app/clearToken');
store.commit('account/clearUserinfo');
store.dispatch('tags/delAllTags');
router.push('/login');
};
return {
userinfo,

View File

@ -11,12 +11,12 @@
</div>
</template>
<script>
import { defineComponent, computed } from "vue";
import Logo from "@/layout/components/Sidebar/Logo.vue";
import Hamburger from "./Hamburger.vue";
import Breadcrumbs from "./Breadcrumbs.vue";
import Userinfo from "./Userinfo.vue";
import { useStore } from "vuex";
import { defineComponent, computed } from 'vue';
import Logo from '@/layout/components/Sidebar/Logo.vue';
import Hamburger from './Hamburger.vue';
import Breadcrumbs from './Breadcrumbs.vue';
import Userinfo from './Userinfo.vue';
import { useStore } from 'vuex';
export default defineComponent({
components: {

View File

@ -1,38 +1,38 @@
import { onBeforeMount, onBeforeUnmount, watch } from "vue"
import { useRouter } from "vue-router"
import { useStore } from "vuex"
import { onBeforeMount, onBeforeUnmount /*watch*/ } from 'vue';
// import { useRouter } from 'vue-router';
import { useStore } from 'vuex';
const WIDTH = 768
const WIDTH = 768;
export const useResizeHandler = () => {
const store = useStore()
const router = useRouter()
const route = router.currentRoute
const store = useStore();
// const router = useRouter();
// const route = router.currentRoute;
const isMobile = () => {
return window.innerWidth < WIDTH
}
return window.innerWidth < WIDTH;
};
const resizeHandler = () => {
if (isMobile()) {
store.commit('app/setDevice', 'mobile')
store.commit('app/setCollapse', 1)
store.commit('app/setDevice', 'mobile');
store.commit('app/setCollapse', 1);
} else {
store.commit('app/setDevice', 'desktop')
store.commit('app/setCollapse', 0)
}
store.commit('app/setDevice', 'desktop');
store.commit('app/setCollapse', 0);
}
};
onBeforeMount(() => {
if (isMobile()) {
store.commit('app/setDevice', 'mobile')
store.commit('app/setCollapse', 1)
store.commit('app/setDevice', 'mobile');
store.commit('app/setCollapse', 1);
}
window.addEventListener('resize', resizeHandler)
})
window.addEventListener('resize', resizeHandler);
});
onBeforeUnmount(() => {
window.removeEventListener('resize', resizeHandler)
})
window.removeEventListener('resize', resizeHandler);
});
// // 监听路由的时候不能使用useRoute获取路由否则会有警告
// watch(route, () => {
@ -40,7 +40,4 @@ export const useResizeHandler = () => {
// store.commit('app/setCollapse', 1)
// }
// })
}
};

View File

@ -7,21 +7,21 @@
<tagsbar />
</div>
<div class="main">
<content />
<Content />
</div>
</div>
</div>
</template>
<script>
import { defineComponent } from "vue";
import Sidebar from "./components/Sidebar/index.vue";
import Topbar from "./components/Topbar/index.vue";
import Tagsbar from "./components/Tagsbar/index.vue";
import Content from "./components/Content/index.vue";
import { useResizeHandler } from "./hooks/useResizeHandler";
import { defineComponent } from 'vue';
import Sidebar from './components/Sidebar/index.vue';
import Topbar from './components/Topbar/index.vue';
import Tagsbar from './components/Tagsbar/index.vue';
import Content from './components/Content/index.vue';
import { useResizeHandler } from './hooks/useResizeHandler';
export default defineComponent({
name: "layout",
name: 'layout',
components: {
Sidebar,
Topbar,

View File

@ -1,29 +1,29 @@
import { createApp } from "vue";
import App from "./App.vue";
import { createApp } from 'vue';
import App from './App.vue';
const app = createApp(App);
// 引入element-plus
import ElementPlus from "element-plus";
import "./assets/style/element-variables.scss";
import ElementPlus from 'element-plus';
import './assets/style/element-variables.scss';
// 引入中文语言包
import "dayjs/locale/zh-cn";
import locale from "element-plus/lib/locale/lang/zh-cn";
import 'dayjs/locale/zh-cn';
import locale from 'element-plus/lib/locale/lang/zh-cn';
// 引入路由
import router from "./router";
import router from './router';
// 引入store
import store from "./store";
import store from './store';
// 权限控制
import "./permission";
import './permission';
// 引入svg图标注册脚本
import "vite-plugin-svg-icons/register";
import 'vite-plugin-svg-icons/register';
// 注册全局组件
import * as Components from "./global-components";
import * as Components from './global-components';
Object.entries(Components).forEach(([key, component]) => {
app.component(key, component);
});
@ -32,4 +32,4 @@ app
.use(ElementPlus, { locale })
.use(store)
.use(router)
.mount("#app");
.mount('#app');

View File

@ -1,49 +1,50 @@
import router from '@/router'
import store from '@/store'
import { TOKEN } from '@/store/modules/app' // TOKEN变量名
import router from '@/router';
import store from '@/store';
import { TOKEN } from '@/store/modules/app'; // TOKEN变量名
const getPageTitle = title => {
const appTitle = store.state.app.title;
if (title) {
return `${title} - ${appTitle}`
}
return appTitle
return `${title} - ${appTitle}`;
}
return appTitle;
};
// 白名单里面是路由对象的name
const WhiteList = ['login', 'forbidden', 'server-error', 'not-found']
const WhiteList = ['login', 'forbidden', 'server-error', 'not-found'];
// vue-router4的路由守卫不再是通过next放行而是通过return返回true或false或者一个路由地址
router.beforeEach(async (to) => {
document.title = getPageTitle(!!to.meta && to.meta.title)
router.beforeEach(async to => {
document.title = getPageTitle(!!to.meta && to.meta.title);
if (WhiteList.includes(to.name)) {
return true
return true;
}
if (!window.localStorage[TOKEN]) {
return {
name: 'login',
query: {
redirect: to.path // redirect是指登录之后可以跳回到redirect指定的页面
redirect: to.path, // redirect是指登录之后可以跳回到redirect指定的页面
},
replace: true
}
replace: true,
};
} else {
let userinfo = store.state.account.userinfo
let userinfo = store.state.account.userinfo;
if (!userinfo) {
try {
// 获取用户信息
userinfo = await store.dispatch("account/getUserinfo");
userinfo = await store.dispatch('account/getUserinfo');
} catch (err) {
return false
return false;
}
}
// 如果没有权限跳转到403页面
if (!!to.meta && !!to.meta.roles && !to.meta.roles.includes(userinfo.role)) {
return { path: '/403', replace: true }
if (
!!to.meta &&
!!to.meta.roles &&
!to.meta.roles.includes(userinfo.role)
) {
return { path: '/403', replace: true };
}
}
})
});

View File

@ -1,18 +1,14 @@
// index.js
import { createRouter, createWebHashHistory } from "vue-router"
import redirect from './modules/redirect'
import error from './modules/error'
import login from './modules/login'
import home from './modules/home'
import test from './modules/test'
import { createRouter, createWebHashHistory } from 'vue-router';
import redirect from './modules/redirect';
import error from './modules/error';
import login from './modules/login';
import home from './modules/home';
import test from './modules/test';
// 左侧菜单
export const allMenus = [
...home,
...test,
]
export const allMenus = [...home, ...test];
const router = createRouter({
history: createWebHashHistory(),
@ -24,13 +20,13 @@ const router = createRouter({
...redirect, // 统一的重定向配置
...login,
...allMenus,
...error
...error,
],
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
return savedPosition;
} else {
return { left: 0, top: 0 }
return { left: 0, top: 0 };
}
},
});

View File

@ -1,18 +1,15 @@
import store from '@/store'
import store from '@/store';
const checkUserinfo = (code) => {
const userinfo = store.state.account.userinfo
if (!!userinfo) {
return `/error/${code}`
}
return true
const checkUserinfo = code => {
const userinfo = store.state.account.userinfo;
if (userinfo) {
return `/error/${code}`;
}
return true;
};
const Layout = () => import('@/layout/index.vue')
const Error = () => import("@/views/error/index.vue");
const Layout = () => import('@/layout/index.vue');
const Error = () => import('@/views/error/index.vue');
export default [
{
@ -25,8 +22,8 @@ export default [
component: Error,
meta: { title: '403' },
props: {
error: '403'
}
error: '403',
},
},
{
path: '500',
@ -34,8 +31,8 @@ export default [
component: Error,
meta: { title: '500' },
props: {
error: '500'
}
error: '500',
},
},
{
path: '404',
@ -43,42 +40,42 @@ export default [
component: Error,
meta: { title: '404' },
props: {
error: '404'
}
}
]
error: '404',
},
},
],
},
{
path: '/403',
name: 'forbidden',
component: Error,
props: {
error: '403'
error: '403',
},
beforeEnter() {
return checkUserinfo('403')
}
return checkUserinfo('403');
},
},
{
path: '/500',
name: 'server-error',
component: Error,
props: {
error: '500'
error: '500',
},
beforeEnter() {
return checkUserinfo('500')
}
return checkUserinfo('500');
},
},
{
path: '/:pathMatch(.*)',
name: 'not-found',
component: Error,
props: {
error: '404'
error: '404',
},
beforeEnter() {
return checkUserinfo('404')
}
return checkUserinfo('404');
},
]
},
];

View File

@ -1,27 +1,26 @@
// home.js
const Layout = () => import('@/layout/index.vue')
const Home = () => import("@/views/home/index.vue");
const Layout = () => import('@/layout/index.vue');
const Home = () => import('@/views/home/index.vue');
export default [
{
path: '/home',
component: Layout,
name: "Dashboard",
name: 'Dashboard',
meta: {
title: "Dashboard",
title: 'Dashboard',
},
icon: 'home',
children: [
{
path: "",
name: "home",
path: '',
name: 'home',
component: Home,
meta: {
title: "首页",
affix: true
}
}
]
title: '首页',
affix: true,
},
]
},
],
},
];

View File

@ -1,10 +1,10 @@
// login.js
const Login = () => import("@/views/login/index.vue");
const Login = () => import('@/views/login/index.vue');
export default [
{
path: "/login",
name: "login",
path: '/login',
name: 'login',
component: Login,
}
]
},
];

View File

@ -1,5 +1,5 @@
const Layout = () => import('@/layout/index.vue')
const Redirect = () => import("@/views/redirect/index.vue");
const Layout = () => import('@/layout/index.vue');
const Redirect = () => import('@/views/redirect/index.vue');
export default [
{
@ -9,7 +9,7 @@ export default [
{
path: '',
component: Redirect,
}
]
}
]
},
],
},
];

View File

@ -1,112 +1,112 @@
const Layout = () => import('@/layout/index.vue')
const List = () => import("@/views/test/index.vue");
const Add = () => import("@/views/test/Add.vue");
const Auth = () => import("@/views/test/Auth.vue");
const NoAuth = () => import("@/views/test/NoAuth.vue");
const Nest = () => import("@/views/test/Nest.vue");
const NestPage1 = () => import("@/views/test/nest/Page1.vue");
const NestPage2 = () => import("@/views/test/nest/Page2.vue");
const Iscache = () => import("@/views/test/Cache.vue");
const Nocache = () => import("@/views/test/Nocache.vue");
const Layout = () => import('@/layout/index.vue');
const List = () => import('@/views/test/index.vue');
const Add = () => import('@/views/test/Add.vue');
const Auth = () => import('@/views/test/Auth.vue');
const NoAuth = () => import('@/views/test/NoAuth.vue');
const Nest = () => import('@/views/test/Nest.vue');
const NestPage1 = () => import('@/views/test/nest/Page1.vue');
const NestPage2 = () => import('@/views/test/nest/Page2.vue');
const Iscache = () => import('@/views/test/Cache.vue');
const Nocache = () => import('@/views/test/Nocache.vue');
export default [
{
path: '/test',
component: Layout,
name: "test",
name: 'test',
meta: {
title: "测试页面",
title: '测试页面',
},
icon: 'el-icon-location',
roles: ["admin", "visitor"],
roles: ['admin', 'visitor'],
children: [
{
path: "",
name: "testList",
path: '',
name: 'testList',
component: List,
meta: {
title: "列表",
roles: ["admin", "visitor"],
title: '列表',
roles: ['admin', 'visitor'],
},
},
{
path: "add",
name: "testAdd",
path: 'add',
name: 'testAdd',
component: Add,
meta: {
title: "添加",
roles: ["admin", "visitor"],
title: '添加',
roles: ['admin', 'visitor'],
},
hidden: true, // 不在菜单中显示
},
{
path: "auth",
name: "testAuth",
path: 'auth',
name: 'testAuth',
component: Auth,
meta: {
title: "权限测试",
roles: ["admin", "visitor"],
}
title: '权限测试',
roles: ['admin', 'visitor'],
},
},
{
path: "noauth",
name: "testNoAuth",
path: 'noauth',
name: 'testNoAuth',
component: NoAuth,
meta: {
title: "权限页面",
roles: ["admin"],
title: '权限页面',
roles: ['admin'],
},
hidden: true
hidden: true,
},
{
path: "cache",
name: "test-cache",
path: 'cache',
name: 'test-cache',
component: Iscache,
meta: {
title: "该页面可缓存",
roles: ["admin", "visitor"]
}
title: '该页面可缓存',
roles: ['admin', 'visitor'],
},
},
{
path: "nocache",
name: "test-no-cache",
path: 'nocache',
name: 'test-no-cache',
component: Nocache,
meta: {
title: "该页面不缓存",
roles: ["admin", "visitor"],
title: '该页面不缓存',
roles: ['admin', 'visitor'],
noCache: true, // 不缓存页面
}
},
},
{
path: "nest",
name: "nest",
path: 'nest',
name: 'nest',
component: Nest,
redirect: '/test/nest/page1',
meta: {
title: "二级菜单",
roles: ["admin", "visitor"],
title: '二级菜单',
roles: ['admin', 'visitor'],
},
children: [
{
path: "page1",
name: "nestPage1",
path: 'page1',
name: 'nestPage1',
component: NestPage1,
meta: {
title: "page1",
roles: ["admin", "visitor"],
title: 'page1',
roles: ['admin', 'visitor'],
},
},
{
path: "page2",
name: "nestPage2",
path: 'page2',
name: 'nestPage2',
component: NestPage2,
meta: {
title: "page2",
roles: ["admin", "visitor"],
title: 'page2',
roles: ['admin', 'visitor'],
},
}
]
},
]
}
]
],
},
],
},
];

View File

@ -1,15 +1,15 @@
//index.js
import { createStore } from "vuex";
import app from "./modules/app";
import account from "./modules/account";
import menu from "./modules/menu";
import tags from "./modules/tags";
import { createStore } from 'vuex';
import app from './modules/app';
import account from './modules/account';
import menu from './modules/menu';
import tags from './modules/tags';
export default createStore({
modules: {
app,
account,
menu,
tags
tags,
},
});

View File

@ -1,9 +1,9 @@
import { GetUserinfo } from '@/api/login'
import { GetUserinfo } from '@/api/login';
export default {
namespaced: true,
state: {
userinfo: null
userinfo: null,
},
mutations: {
// 保存用户信息
@ -13,16 +13,16 @@ export default {
// 清除用户信息
clearUserinfo(state) {
state.userinfo = null;
}
},
},
actions: {
// 获取用户信息
async getUserinfo({ commit }) {
const { code, data } = await GetUserinfo();
if (+code === 200) {
commit("setUserinfo", data);
return Promise.resolve(data)
}
commit('setUserinfo', data);
return Promise.resolve(data);
}
},
},
};

View File

@ -1,6 +1,6 @@
import { getItem, setItem, removeItem } from "@/utils/storage"; //getItem和setItem是封装的操作localStorage的方法
export const TOKEN = "VEA-TOKEN";
const COLLAPSE = "VEA-COLLAPSE";
import { getItem, setItem, removeItem } from '@/utils/storage'; //getItem和setItem是封装的操作localStorage的方法
export const TOKEN = 'VEA-TOKEN';
const COLLAPSE = 'VEA-COLLAPSE';
export default {
namespaced: true,
@ -8,7 +8,7 @@ export default {
title: 'Vue3 Element Admin',
authorization: getItem(TOKEN),
sidebar: {
collapse: getItem(COLLAPSE)
collapse: getItem(COLLAPSE),
},
device: 'desktop',
},
@ -34,7 +34,7 @@ export default {
removeItem(COLLAPSE);
},
setDevice(state, device) {
state.device = device
state.device = device;
},
},
actions: {},

View File

@ -1,16 +1,20 @@
import { allMenus } from '@/router'
import { GetMenus } from '@/api/menu'
import { allMenus } from '@/router';
// import { GetMenus } from '@/api/menu';
const hasPermission = (role, route) => {
if (!!route.meta && !!route.meta.roles && !route.meta.roles.includes(role)) {
return false
}
return true
return false;
}
return true;
};
const generateUrl = (path, parentPath) => {
return path.startsWith('/') ? path : (!!path ? `${parentPath}/${path}` : parentPath)
}
return path.startsWith('/')
? path
: path
? `${parentPath}/${path}`
: parentPath;
};
const getFilterMenus = (arr, role, parentPath = '') => {
const menus = [];
@ -21,43 +25,42 @@ const getFilterMenus = (arr, role, parentPath = '') => {
url: generateUrl(item.path, parentPath),
title: item.meta.title,
icon: item.icon,
}
};
if (item.children) {
if (item.children.length === 1) {
menu.url = generateUrl(item.children[0].path, menu.url)
menu.url = generateUrl(item.children[0].path, menu.url);
} else {
menu.children = getFilterMenus(item.children, role, menu.url)
menu.children = getFilterMenus(item.children, role, menu.url);
}
}
menus.push(menu)
menus.push(menu);
}
})
});
return menus
}
return menus;
};
export default {
namespaced: true,
state: {
menus: []
menus: [],
},
mutations: {
SET_MENUS(state, data) {
state.menus = data;
}
},
},
actions: {
async generateMenus({ commit }, role) {
// 方式一:根据角色生成菜单
const menus = getFilterMenus(allMenus, role)
commit('SET_MENUS', menus)
const menus = getFilterMenus(allMenus, role);
commit('SET_MENUS', menus);
// // 方式二:从后台获取菜单
// const { code, data } = await GetMenus();
// if (+code === 200) {
// commit('SET_MENUS', data)
// }
}
},
},
};

View File

@ -1,12 +1,12 @@
import { getItem, setItem, removeItem } from "@/utils/storage"; //getItem和setItem是封装的操作localStorage的方法
import { getItem, setItem, removeItem } from '@/utils/storage'; //getItem和setItem是封装的操作localStorage的方法
const TAGLIST = 'VEA-TAGLIST'
const TAGLIST = 'VEA-TAGLIST';
const state = {
tagList: getItem(TAGLIST) || [],
cacheList: [],
activePosition: 0
}
activePosition: 0,
};
const mutations = {
ADD_TAG_LIST: (state, { path, fullPath, name, meta }) => {
@ -15,138 +15,144 @@ const mutations = {
state.tagList.splice(
state.activePosition + 1,
0,
Object.assign({}, { path, fullPath, name, meta }, {
Object.assign(
{},
{ path, fullPath, name, meta },
{
title: meta.title || '未命名',
fullPath: fullPath || path
})
fullPath: fullPath || path,
}
)
);
// 保存到localStorage
setItem(TAGLIST, state.tagList);
},
ADD_CACHE_LIST: (state, tag) => {
if (state.cacheList.includes(tag.name)) return
if (state.cacheList.includes(tag.name)) return;
if (!tag.meta.noCache) {
state.cacheList.push(tag.name)
state.cacheList.push(tag.name);
}
},
DEL_TAG_LIST: (state, tag) => {
state.tagList = state.tagList.filter(v => v.path !== tag.path)
state.tagList = state.tagList.filter(v => v.path !== tag.path);
// 保存到localStorage
setItem(TAGLIST, state.tagList);
},
DEL_CACHE_LIST: (state, tag) => {
state.cacheList = state.cacheList.filter(v => v !== tag.name)
state.cacheList = state.cacheList.filter(v => v !== tag.name);
},
DEL_OTHER_TAG_LIST: (state, tag) => {
state.tagList = state.tagList.filter(v => !!v.meta.affix || v.path === tag.path)
state.tagList = state.tagList.filter(
v => !!v.meta.affix || v.path === tag.path
);
// 保存到localStorage
setItem(TAGLIST, state.tagList);
},
DEL_OTHER_CACHE_LIST: (state, tag) => {
state.cacheList = state.cacheList.filter(v => v === tag.name)
state.cacheList = state.cacheList.filter(v => v === tag.name);
},
DEL_SOME_TAG_LIST: (state, tags) => {
state.tagList = state.tagList.filter(v => !!v.meta.affix || tags.every(tag => tag.path !== v.path))
state.tagList = state.tagList.filter(
v => !!v.meta.affix || tags.every(tag => tag.path !== v.path)
);
// 保存到localStorage
setItem(TAGLIST, state.tagList);
},
DEL_SOME_CACHE_LIST: (state, tags) => {
state.cacheList = state.cacheList.filter(v => tags.every(tag => tag.name !== v))
state.cacheList = state.cacheList.filter(v =>
tags.every(tag => tag.name !== v)
);
},
DEL_ALL_TAG_LIST: state => {
state.tagList = state.tagList.filter(v => !!v.meta.affix)
state.tagList = state.tagList.filter(v => !!v.meta.affix);
// 保存到localStorage
removeItem(TAGLIST);
},
DEL_ALL_CACHE_LIST: state => {
state.cacheList = []
state.cacheList = [];
},
UPDATE_TAG_LIST: (state, tag) => {
const index = state.tagList.findIndex(v => v.path === tag.path);
if (index > -1) {
state.tagList[index] = Object.assign({}, state.tagList[index], tag)
state.tagList[index] = Object.assign({}, state.tagList[index], tag);
// 保存到localStorage
setItem(TAGLIST, state.tagList);
}
},
SAVE_ACTIVE_POSITION: (state, index) => {
state.activePosition = index
state.activePosition = index;
},
}
};
const actions = {
saveActivePosition({ commit }, index) {
commit('SAVE_ACTIVE_POSITION', index)
commit('SAVE_ACTIVE_POSITION', index);
},
addTag({ dispatch }, tag) {
dispatch('addTagList', tag)
dispatch('addCacheList', tag)
dispatch('addTagList', tag);
dispatch('addCacheList', tag);
},
addTagList({ commit }, tag) {
commit('ADD_TAG_LIST', tag)
commit('ADD_TAG_LIST', tag);
},
addCacheList({ commit }, tag) {
commit('ADD_CACHE_LIST', tag)
commit('ADD_CACHE_LIST', tag);
},
delTag({ dispatch }, tag) {
dispatch('delTagList', tag)
dispatch('delCacheList', tag)
dispatch('delTagList', tag);
dispatch('delCacheList', tag);
},
delTagList({ commit }, tag) {
commit('DEL_TAG_LIST', tag)
commit('DEL_TAG_LIST', tag);
},
delCacheList({ commit }, tag) {
commit('DEL_CACHE_LIST', tag)
commit('DEL_CACHE_LIST', tag);
},
delOtherTags({ dispatch }, tag) {
dispatch('delOtherTagList', tag)
dispatch('delOtherCacheList', tag)
dispatch('delOtherTagList', tag);
dispatch('delOtherCacheList', tag);
},
delOtherTagList({ commit }, tag) {
commit('DEL_OTHER_TAG_LIST', tag)
commit('DEL_OTHER_TAG_LIST', tag);
},
delOtherCacheList({ commit }, tag) {
commit('DEL_OTHER_CACHE_LIST', tag)
commit('DEL_OTHER_CACHE_LIST', tag);
},
delSomeTags({ commit }, tags) {
commit('DEL_SOME_TAG_LIST', tags)
commit('DEL_SOME_CACHE_LIST', tags)
commit('DEL_SOME_TAG_LIST', tags);
commit('DEL_SOME_CACHE_LIST', tags);
},
delAllTags({ dispatch }) {
dispatch('delAllTagList')
dispatch('delAllCacheList')
dispatch('delAllTagList');
dispatch('delAllCacheList');
},
delAllTagList({ commit }) {
commit('DEL_ALL_TAG_LIST')
commit('DEL_ALL_TAG_LIST');
},
delAllCacheList({ commit }) {
commit('DEL_ALL_CACHE_LIST')
commit('DEL_ALL_CACHE_LIST');
},
updateTagList({ commit }, tag) {
commit('UPDATE_TAG_LIST', tag)
}
}
commit('UPDATE_TAG_LIST', tag);
},
};
export default {
namespaced: true,
state,
mutations,
actions
}
actions,
};

View File

@ -1,24 +1,24 @@
import axios from "axios";
import { ElMessage } from "element-plus";
import store from "@/store";
import router from "@/router";
import axios from 'axios';
import { ElMessage } from 'element-plus';
import store from '@/store';
import router from '@/router';
const service = axios.create({
baseURL: "/",
baseURL: '/',
timeout: 10000,
withCredentials: true,
});
// 拦截请求
service.interceptors.request.use(
(config) => {
config => {
const { authorization } = store.state.app;
if (authorization) {
config.headers.Authorization = `Bearer ${authorization.token}`;
}
return config;
},
(error) => {
error => {
// console.log(error);
return Promise.reject(error);
}
@ -27,18 +27,18 @@ service.interceptors.request.use(
// 拦截响应
service.interceptors.response.use(
// 响应成功进入第1个函数该函数的参数是响应对象
(response) => {
response => {
return response.data;
},
// 响应失败进入第2个函数该函数的参数是错误对象
async (error) => {
async error => {
// 如果响应码是 401 ,则请求获取新的 token
// 响应拦截器中的 error 就是那个响应的错误对象
if (error.response && error.response.status === 401) {
// 校验是否有 refresh_token
const { authorization } = store.state.app;
if (!authorization || !authorization.refresh_token) {
router.push("/login");
router.push('/login');
// 代码不要往后执行了
return Promise.reject(error);
@ -46,8 +46,8 @@ service.interceptors.response.use(
// 如果有refresh_token则请求获取新的 token
try {
const res = await axios({
method: "PUT",
url: "/api/authorizations",
method: 'PUT',
url: '/api/authorizations',
timeout: 10000,
headers: {
Authorization: `Bearer ${authorization.refresh_token}`,
@ -55,7 +55,7 @@ service.interceptors.response.use(
});
// 如果获取成功,则把新的 token 更新到容器中
// console.log('刷新 token 成功', res)
store.commit("app/setToken", {
store.commit('app/setToken', {
token: res.data.data.token, // 最新获取的可用 token
refresh_token: authorization.refresh_token, // 还是原来的 refresh_token
});
@ -66,9 +66,9 @@ service.interceptors.response.use(
} catch (err) {
// 如果获取失败,直接跳转 登录页
// console.log('请求刷新 token 失败', err)
router.push("/login");
router.push('/login');
// 清除token
store.commit("app/clearToken")
store.commit('app/clearToken');
return Promise.reject(error);
}
}

View File

@ -8,7 +8,7 @@ export const getItem = name => {
};
export const setItem = (name, value) => {
if (typeof value === "object") {
if (typeof value === 'object') {
value = JSON.stringify(value);
}

View File

@ -21,14 +21,13 @@
</template>
<script>
import { defineComponent } from "vue";
import { defineComponent } from 'vue';
export default defineComponent({
props: ["error"],
props: ['error'],
});
</script>
<style lang="scss" scoped>
.error {
position: relative;

View File

@ -3,10 +3,10 @@
</template>
<script>
import { defineComponent } from "vue";
import { defineComponent } from 'vue';
export default defineComponent({
name: "home",
name: 'home',
setup() {},
});
</script>

View File

@ -43,12 +43,12 @@ import {
toRefs,
ref,
computed,
} from "vue";
import { Login } from "@/api/login";
import { useStore } from "vuex";
import { useRouter, useRoute } from "vue-router";
} from 'vue';
import { Login } from '@/api/login';
import { useStore } from 'vuex';
import { useRouter, useRoute } from 'vue-router';
export default defineComponent({
name: "login",
name: 'login',
setup() {
const { ctx } = getCurrentInstance(); // ctxvue2this
const store = useStore();
@ -56,42 +56,42 @@ export default defineComponent({
const route = useRoute();
const state = reactive({
model: {
userName: "admin",
password: "123456",
userName: 'admin',
password: '123456',
},
rules: {
userName: [
{ required: true, message: "请输入用户名", trigger: "blur" },
{ required: true, message: '请输入用户名', trigger: 'blur' },
],
password: [
{ required: true, message: "请输入密码", trigger: "blur" },
{ required: true, message: '请输入密码', trigger: 'blur' },
{
min: 6,
max: 12,
message: "长度在 6 到 12 个字符",
trigger: "blur",
message: '长度在 6 到 12 个字符',
trigger: 'blur',
},
],
},
loading: false,
btnText: computed(() => (state.loading ? "登录中..." : "登录")),
btnText: computed(() => (state.loading ? '登录中...' : '登录')),
loginForm: ref(null),
submit: () => {
if (state.loading) {
return;
}
state.loginForm.validate(async (valid) => {
state.loginForm.validate(async valid => {
if (valid) {
state.loading = true;
const { code, data, message } = await Login(state.model);
if (+code === 200) {
ctx.$message.success({
message: "登录成功",
message: '登录成功',
duration: 1000,
});
const targetPath = route.query.redirect;
router.push(!!targetPath ? targetPath : "/");
store.commit("app/setToken", data);
router.push(targetPath ? targetPath : '/');
store.commit('app/setToken', data);
} else {
ctx.$message.error(message);
}

View File

@ -1,8 +1,10 @@
<script>
export default {
created() {
this.$router.replace(this.$route.fullPath.replace(/^\/redirect/, ""));
this.$router.replace(this.$route.fullPath.replace(/^\/redirect/, ''));
},
render(h) {
return h();
},
render() {},
};
</script>

View File

@ -6,14 +6,14 @@
</dl>
</template>
<script>
import { defineComponent, onActivated } from "vue";
import { defineComponent, onActivated } from 'vue';
export default defineComponent({
name: "test-cache", // namename
name: 'test-cache', // namename
setup() {
console.log("cache");
console.log('cache');
onActivated(() => {
console.log("onActivated");
console.log('onActivated');
});
},
});

View File

@ -6,12 +6,12 @@
</dl>
</template>
<script>
import { defineComponent } from "vue";
import { defineComponent } from 'vue';
export default defineComponent({
name: "test-no-cache", // namenamename
name: 'test-no-cache', // namenamename
setup() {
console.log("nocache");
console.log('nocache');
},
});
</script>

View File

@ -32,170 +32,170 @@
</template>
<script>
import { defineComponent, reactive, ref, toRefs } from "vue";
import { defineComponent, reactive, ref, toRefs } from 'vue';
export default defineComponent({
name: "testList",
name: 'testList',
setup() {
const state = reactive({
// el-table-column
columns: [
{ type: "selection" },
{ label: "序号", type: "index" },
{ label: "名称", prop: "nickName", sortable: true, width: 180 },
{ label: "邮箱", prop: "userEmail" },
{ type: 'selection' },
{ label: '序号', type: 'index' },
{ label: '名称', prop: 'nickName', sortable: true, width: 180 },
{ label: '邮箱', prop: 'userEmail' },
{
label: "操作",
fixed: "right",
label: '操作',
fixed: 'right',
width: 180,
align: "center",
tdSlot: "operate", //
align: 'center',
tdSlot: 'operate', //
},
],
//
searchConfig: {
labelWidth: "90px", //
inputWidth: "360px", //
labelWidth: '90px', //
inputWidth: '360px', //
fields: [
{
type: "text",
label: "账户名称",
name: "nickName",
defaultValue: "abc",
type: 'text',
label: '账户名称',
name: 'nickName',
defaultValue: 'abc',
},
{
type: "textarea",
label: "描述",
name: "description",
type: 'textarea',
label: '描述',
name: 'description',
},
{
label: "状态",
name: "status",
type: "select",
label: '状态',
name: 'status',
type: 'select',
defaultValue: 1,
options: [
{
name: "已发布",
name: '已发布',
value: 1,
},
{
name: "未发布",
name: '未发布',
value: 0,
},
],
},
{
label: "性别",
name: "sex",
type: "radio",
label: '性别',
name: 'sex',
type: 'radio',
options: [
{
name: "男",
name: '男',
value: 1,
},
{
name: "女",
name: '女',
value: 0,
},
],
},
{
label: "城市",
name: "city",
type: "radio-button",
label: '城市',
name: 'city',
type: 'radio-button',
options: [
{
name: "北京",
value: "bj",
name: '北京',
value: 'bj',
},
{
name: "上海",
value: "sh",
name: '上海',
value: 'sh',
},
{
name: "广州",
value: "gz",
name: '广州',
value: 'gz',
},
{
name: "深圳",
value: "sz",
name: '深圳',
value: 'sz',
},
],
},
{
label: "爱好",
name: "hobby",
type: "checkbox",
defaultValue: ["吃饭"],
label: '爱好',
name: 'hobby',
type: 'checkbox',
defaultValue: ['吃饭'],
options: [
{
name: "吃饭",
value: "吃饭",
name: '吃饭',
value: '吃饭',
},
{
name: "睡觉",
value: "睡觉",
name: '睡觉',
value: '睡觉',
},
{
name: "打豆豆",
value: "打豆豆",
name: '打豆豆',
value: '打豆豆',
},
],
// transform: (val) => val.join(","),
},
{
label: "水果",
name: "fruit",
type: "checkbox-button",
label: '水果',
name: 'fruit',
type: 'checkbox-button',
options: [
{
name: "苹果",
value: "苹果",
name: '苹果',
value: '苹果',
},
{
name: "香蕉",
value: "香蕉",
name: '香蕉',
value: '香蕉',
},
{
name: "橘子",
value: "橘子",
name: '橘子',
value: '橘子',
},
{
name: "葡萄",
value: "葡萄",
name: '葡萄',
value: '葡萄',
},
],
transform: (val) => val.join(","),
transform: val => val.join(','),
},
{
label: "日期",
name: "date",
type: "date",
label: '日期',
name: 'date',
type: 'date',
},
{
label: "时间",
name: "datetime",
type: "datetime",
defaultValue: "2020-10-10 8:00:00",
label: '时间',
name: 'datetime',
type: 'datetime',
defaultValue: '2020-10-10 8:00:00',
},
{
label: "日期范围",
name: "daterange",
type: "daterange",
trueNames: ["startDate", "endDate"],
label: '日期范围',
name: 'daterange',
type: 'daterange',
trueNames: ['startDate', 'endDate'],
},
{
label: "时间范围",
name: "datetimerange",
type: "datetimerange",
trueNames: ["startTime", "endTime"],
style: { width: "360px" },
defaultValue: ["2020-10-10 9:00:00", "2020-10-11 18:30:00"],
label: '时间范围',
name: 'datetimerange',
type: 'datetimerange',
trueNames: ['startTime', 'endTime'],
style: { width: '360px' },
defaultValue: ['2020-10-10 9:00:00', '2020-10-11 18:30:00'],
},
{
label: "数量",
name: "num",
type: "number",
label: '数量',
name: 'num',
type: 'number',
min: 0,
max: 10,
},
@ -203,10 +203,10 @@ export default defineComponent({
},
//
paginationConfig: {
layout: "total, prev, pager, next, sizes", //
layout: 'total, prev, pager, next, sizes', //
pageSize: 5, //
pageSizes: [5, 10, 20, 50],
style: { textAlign: "left" },
style: { textAlign: 'left' },
},
selectedItems: [],
batchDelete() {
@ -220,7 +220,7 @@ export default defineComponent({
async getList(params) {
console.log(params);
// params
const { data } = await new Promise((rs) => {
const { data } = await new Promise(rs => {
setTimeout(() => {
rs({
code: 200,
@ -228,13 +228,13 @@ export default defineComponent({
list: [
{
id: 1,
nickName: "zhangsan",
userEmail: "zhangsan@xx.com",
nickName: 'zhangsan',
userEmail: 'zhangsan@xx.com',
},
{
id: 2,
nickName: "lisi",
userEmail: "lisi@xx.com",
nickName: 'lisi',
userEmail: 'lisi@xx.com',
},
],
total: 100,

View File

@ -1,8 +1,8 @@
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import path from "path";
import { viteMockServe } from "vite-plugin-mock";
import viteSvgIcons from "vite-plugin-svg-icons";
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import path from 'path';
import { viteMockServe } from 'vite-plugin-mock';
import viteSvgIcons from 'vite-plugin-svg-icons';
// https://vitejs.dev/config/
export default env => {
@ -13,11 +13,11 @@ export default env => {
plugins: [
vue(),
viteMockServe({
ignore: /^\_/, // 忽略以下划线`_`开头的文件
mockPath: "mock", // 指定mock目录中的文件全部是mock接口
ignore: /^_/, // 忽略以下划线`_`开头的文件
mockPath: 'mock', // 指定mock目录中的文件全部是mock接口
supportTs: false, // mockPath目录中的文件是否支持ts文件现在我们不使用ts所以设为false
localEnabled: env.mode === "mock", // 开发环境是否开启mock功能可以在package.json的启动命令中指定mode为mock
prodEnabled: env.mode === "mock", // 生产环境是否开启mock功能
localEnabled: env.mode === 'mock', // 开发环境是否开启mock功能可以在package.json的启动命令中指定mode为mock
prodEnabled: env.mode === 'mock', // 生产环境是否开启mock功能
injectCode: `
import { setupProdMockServer } from '../mock/_createProductionServer';
setupProdMockServer();
@ -25,9 +25,9 @@ export default env => {
}),
viteSvgIcons({
// 指定需要缓存的图标文件夹
iconDirs: [path.resolve(__dirname, "src/assets/svg")],
iconDirs: [path.resolve(__dirname, 'src/assets/svg')],
// 指定symbolId格式
symbolId: "icon-[dir]-[name]",
symbolId: 'icon-[dir]-[name]',
}),
],
css: {
@ -40,14 +40,14 @@ export default env => {
},
resolve: {
alias: {
"@": path.resolve(__dirname, "src"),
'@': path.resolve(__dirname, 'src'),
},
},
server: {
open: true,
proxy: {
"/api": {
target: "http://dev.erp.com", // 后端接口的域名
'/api': {
target: 'http://dev.erp.com', // 后端接口的域名
changeOrigin: true,
},
},
@ -67,8 +67,8 @@ export default env => {
output: {
// 拆分单独模块
manualChunks: {
"element-plus": ["element-plus"],
mockjs: ["mockjs"],
'element-plus': ['element-plus'],
mockjs: ['mockjs'],
},
},
},