commit
6013211a09
@ -3,6 +3,7 @@ import qs from 'query-string';
|
|||||||
import { ListParam as DeptParam } from '@/api/system/dept';
|
import { ListParam as DeptParam } from '@/api/system/dept';
|
||||||
import { ListParam as MenuParam } from '@/api/system/menu';
|
import { ListParam as MenuParam } from '@/api/system/menu';
|
||||||
import { ListParam as RoleParam } from '@/api/system/role';
|
import { ListParam as RoleParam } from '@/api/system/role';
|
||||||
|
import { ListParam as OptionParam } from '@/api/system/config';
|
||||||
import { TreeNodeData } from '@arco-design/web-vue';
|
import { TreeNodeData } from '@arco-design/web-vue';
|
||||||
import { LabelValueState } from '@/store/modules/dict/types';
|
import { LabelValueState } from '@/store/modules/dict/types';
|
||||||
|
|
||||||
@ -39,6 +40,15 @@ export function listDict(code: string) {
|
|||||||
return axios.get<LabelValueState[]>(`${BASE_URL}/dict/${code}`);
|
return axios.get<LabelValueState[]>(`${BASE_URL}/dict/${code}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function listOption(params: OptionParam) {
|
||||||
|
return axios.get<LabelValueState[]>(`${BASE_URL}/option`, {
|
||||||
|
params,
|
||||||
|
paramsSerializer: (obj) => {
|
||||||
|
return qs.stringify(obj);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function upload(data: FormData) {
|
export function upload(data: FormData) {
|
||||||
return axios.post(`${BASE_URL}/file`, data);
|
return axios.post(`${BASE_URL}/file`, data);
|
||||||
}
|
}
|
||||||
|
@ -1,28 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<a-layout-footer class="footer">
|
<a-layout-footer class="footer">
|
||||||
{{ `Copyright © 2022-${new Date().getFullYear()}` }}
|
<div v-html="appStore.getCopyright"></div>
|
||||||
<a
|
|
||||||
href="https://blog.charles7c.top/about/me"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener"
|
|
||||||
>
|
|
||||||
Charles7c
|
|
||||||
</a>
|
|
||||||
<span> ⋅ </span>
|
|
||||||
<a
|
|
||||||
href="https://github.com/Charles7c/continew-admin"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener"
|
|
||||||
>{{ $t('title') }}</a
|
|
||||||
> v1.2.0-SNAPSHOT
|
|
||||||
<span> ⋅ </span>
|
|
||||||
<a href="https://beian.miit.gov.cn" target="_blank" rel="noopener">
|
|
||||||
津ICP备2022005864号-2
|
|
||||||
</a>
|
|
||||||
</a-layout-footer>
|
</a-layout-footer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup></script>
|
<script lang="ts" setup>
|
||||||
|
import { useAppStore } from '@/store';
|
||||||
|
|
||||||
|
const appStore = useAppStore();
|
||||||
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.footer {
|
.footer {
|
||||||
@ -33,13 +19,4 @@
|
|||||||
color: var(--color-text-2);
|
color: var(--color-text-2);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
|
||||||
text-decoration: none;
|
|
||||||
color: var(--color-text-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
color: rgb(var(--gray-6));
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -2,12 +2,12 @@
|
|||||||
<div class="navbar">
|
<div class="navbar">
|
||||||
<div class="left-side">
|
<div class="left-side">
|
||||||
<a-space>
|
<a-space>
|
||||||
<img alt="logo" src="/logo.svg" />
|
<img alt="logo" :src="getFile(appStore.getLogo)" height="33"/>
|
||||||
<a-typography-title
|
<a-typography-title
|
||||||
:style="{ margin: 0, fontSize: '18px' }"
|
:style="{ margin: 0, fontSize: '18px' }"
|
||||||
:heading="5"
|
:heading="5"
|
||||||
>
|
>
|
||||||
{{ $t('title') }}
|
{{ appStore.getTitle }}
|
||||||
</a-typography-title>
|
</a-typography-title>
|
||||||
<icon-menu-fold
|
<icon-menu-fold
|
||||||
v-if="!topMenu && appStore.device === 'mobile'"
|
v-if="!topMenu && appStore.device === 'mobile'"
|
||||||
@ -198,6 +198,7 @@
|
|||||||
import useUser from '@/hooks/user';
|
import useUser from '@/hooks/user';
|
||||||
import Menu from '@/components/menu/index.vue';
|
import Menu from '@/components/menu/index.vue';
|
||||||
import getAvatar from '@/utils/avatar';
|
import getAvatar from '@/utils/avatar';
|
||||||
|
import getFile from '@/utils/file';
|
||||||
import MessageBox from '../message-box/index.vue';
|
import MessageBox from '../message-box/index.vue';
|
||||||
|
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
import type { Router, LocationQueryRaw } from 'vue-router';
|
import type { Router, LocationQueryRaw } from 'vue-router';
|
||||||
import NProgress from 'nprogress'; // progress bar
|
import NProgress from 'nprogress'; // progress bar
|
||||||
|
|
||||||
import { useLoginStore } from '@/store';
|
import { useLoginStore, useAppStore } from '@/store';
|
||||||
import { isLogin } from '@/utils/auth';
|
import { isLogin } from '@/utils/auth';
|
||||||
|
|
||||||
export default function setupUserLoginInfoGuard(router: Router) {
|
export default function setupUserLoginInfoGuard(router: Router) {
|
||||||
router.beforeEach(async (to, from, next) => {
|
router.beforeEach(async (to, from, next) => {
|
||||||
NProgress.start();
|
NProgress.start();
|
||||||
const loginStore = useLoginStore();
|
const loginStore = useLoginStore();
|
||||||
|
const appStore = useAppStore();
|
||||||
|
appStore.init();
|
||||||
if (isLogin()) {
|
if (isLogin()) {
|
||||||
if (loginStore.roles[0]) {
|
if (loginStore.roles[0]) {
|
||||||
next();
|
next();
|
||||||
|
@ -9,7 +9,9 @@ import type { MessageReturn } from '@arco-design/web-vue/es/message/interface';
|
|||||||
import type { RouteRecordNormalized } from 'vue-router';
|
import type { RouteRecordNormalized } from 'vue-router';
|
||||||
import defaultSettings from '@/config/settings.json';
|
import defaultSettings from '@/config/settings.json';
|
||||||
import { listRoute } from '@/api/auth/login';
|
import { listRoute } from '@/api/auth/login';
|
||||||
import { AppState } from './types';
|
import { listOption } from '@/api/common';
|
||||||
|
import getFile from '@/utils/file';
|
||||||
|
import { AppState, Config } from './types';
|
||||||
|
|
||||||
const recursionMenu = (
|
const recursionMenu = (
|
||||||
appMenu: RouteRecordNormalized[],
|
appMenu: RouteRecordNormalized[],
|
||||||
@ -45,6 +47,18 @@ const useAppStore = defineStore('app', {
|
|||||||
);
|
);
|
||||||
return menuList;
|
return menuList;
|
||||||
},
|
},
|
||||||
|
getLogo(state: AppState): string | undefined {
|
||||||
|
return state.config?.site_logo;
|
||||||
|
},
|
||||||
|
getFavicon(state: AppState): string | undefined {
|
||||||
|
return state.config?.site_favicon;
|
||||||
|
},
|
||||||
|
getTitle(state: AppState): string | undefined {
|
||||||
|
return state.config?.site_title;
|
||||||
|
},
|
||||||
|
getCopyright(state: AppState): string | undefined {
|
||||||
|
return state.config?.site_copyright;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
@ -97,6 +111,51 @@ const useAppStore = defineStore('app', {
|
|||||||
clearServerMenu() {
|
clearServerMenu() {
|
||||||
this.serverMenu = [];
|
this.serverMenu = [];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化系统配置信息
|
||||||
|
*/
|
||||||
|
init() {
|
||||||
|
listOption({
|
||||||
|
code: ['site_title', 'site_copyright', 'site_favicon', 'site_logo'],
|
||||||
|
}).then((res) => {
|
||||||
|
const resMap = new Map();
|
||||||
|
res.data.forEach((item) => {
|
||||||
|
resMap.set(item.label, item.value);
|
||||||
|
});
|
||||||
|
this.config = {
|
||||||
|
site_title: resMap.get('site_title'),
|
||||||
|
site_copyright: resMap.get('site_copyright'),
|
||||||
|
site_logo: resMap.get('site_logo'),
|
||||||
|
site_favicon: resMap.get('site_logo'),
|
||||||
|
};
|
||||||
|
document.title = resMap.get('site_title');
|
||||||
|
document
|
||||||
|
.querySelector('link[rel="shortcut icon"]')
|
||||||
|
?.setAttribute(
|
||||||
|
'href',
|
||||||
|
getFile(resMap.get('site_favicon')) ||
|
||||||
|
'https://cnadmin.charles7c.top/favicon.ico'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存系统配置
|
||||||
|
*
|
||||||
|
* @param config 系统配置
|
||||||
|
*/
|
||||||
|
save(config: Config) {
|
||||||
|
this.$state.config = config;
|
||||||
|
document.title = config.site_title || '';
|
||||||
|
document
|
||||||
|
.querySelector('link[rel="shortcut icon"]')
|
||||||
|
?.setAttribute(
|
||||||
|
'href',
|
||||||
|
getFile(config.site_favicon) ||
|
||||||
|
'https://cnadmin.charles7c.top/favicon.ico'
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
import type { RouteRecordNormalized } from 'vue-router';
|
import type { RouteRecordNormalized } from 'vue-router';
|
||||||
|
|
||||||
|
export interface Config {
|
||||||
|
site_title?: string;
|
||||||
|
site_copyright?: string;
|
||||||
|
site_logo?: string;
|
||||||
|
site_favicon?: string;
|
||||||
|
}
|
||||||
export interface AppState {
|
export interface AppState {
|
||||||
theme: string;
|
theme: string;
|
||||||
colorWeak: boolean;
|
colorWeak: boolean;
|
||||||
@ -18,4 +24,5 @@ export interface AppState {
|
|||||||
menuFromServer: boolean;
|
menuFromServer: boolean;
|
||||||
serverMenu: RouteRecordNormalized[];
|
serverMenu: RouteRecordNormalized[];
|
||||||
[key: string]: unknown;
|
[key: string]: unknown;
|
||||||
|
config?: Config;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="login-form-wrapper">
|
<div class="login-form-wrapper">
|
||||||
<div class="login-form-title">{{ $t('login.form.title') }}</div>
|
<div class="login-form-title">登录 {{ appStore.getTitle }}</div>
|
||||||
<div class="login-form-sub-title">{{ $t('login.form.subTitle') }}</div>
|
<div class="login-form-sub-title">{{ $t('login.form.subTitle') }}</div>
|
||||||
<a-form
|
<a-form
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
@ -71,7 +71,7 @@
|
|||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { useStorage } from '@vueuse/core';
|
import { useStorage } from '@vueuse/core';
|
||||||
import { useLoginStore } from '@/store';
|
import { useLoginStore, useAppStore } from '@/store';
|
||||||
import { encryptByRsa } from '@/utils/encrypt';
|
import { encryptByRsa } from '@/utils/encrypt';
|
||||||
// import debug from '@/utils/env';
|
// import debug from '@/utils/env';
|
||||||
|
|
||||||
@ -79,6 +79,7 @@
|
|||||||
|
|
||||||
const captchaImgBase64 = ref('');
|
const captchaImgBase64 = ref('');
|
||||||
const loginStore = useLoginStore();
|
const loginStore = useLoginStore();
|
||||||
|
const appStore = useAppStore();
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="logo">
|
<div class="logo">
|
||||||
<img src="/logo.svg" alt="logo" />
|
<img :src="getFile(appStore.getLogo)" alt="logo" height="33" />
|
||||||
<div class="logo-text">{{ $t('title') }}</div>
|
<div class="logo-text">{{ appStore.getTitle }}</div>
|
||||||
</div>
|
</div>
|
||||||
<LoginBanner />
|
<LoginBanner />
|
||||||
<div class="content">
|
<div class="content">
|
||||||
@ -18,8 +18,12 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import Footer from '@/components/footer/index.vue';
|
import Footer from '@/components/footer/index.vue';
|
||||||
|
import { useAppStore } from '@/store';
|
||||||
|
import getFile from '@/utils/file';
|
||||||
import LoginBanner from './components/banner.vue';
|
import LoginBanner from './components/banner.vue';
|
||||||
import LoginForm from './components/login-form.vue';
|
import LoginForm from './components/login-form.vue';
|
||||||
|
|
||||||
|
const appStore = useAppStore();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
@ -170,6 +170,7 @@
|
|||||||
} from '@/api/system/config';
|
} from '@/api/system/config';
|
||||||
import { upload } from '@/api/common';
|
import { upload } from '@/api/common';
|
||||||
import getFile from '@/utils/file';
|
import getFile from '@/utils/file';
|
||||||
|
import { useAppStore } from '@/store';
|
||||||
|
|
||||||
const { proxy } = getCurrentInstance() as any;
|
const { proxy } = getCurrentInstance() as any;
|
||||||
const dataList = ref<DataRecord[]>([]);
|
const dataList = ref<DataRecord[]>([]);
|
||||||
@ -180,6 +181,7 @@
|
|||||||
const siteCopyright = ref<DataRecord>();
|
const siteCopyright = ref<DataRecord>();
|
||||||
const siteLogo = ref<DataRecord>();
|
const siteLogo = ref<DataRecord>();
|
||||||
const siteFavicon = ref<DataRecord>();
|
const siteFavicon = ref<DataRecord>();
|
||||||
|
const appStore = useAppStore();
|
||||||
|
|
||||||
const data = reactive({
|
const data = reactive({
|
||||||
queryParams: {
|
queryParams: {
|
||||||
@ -251,7 +253,7 @@
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
save(optionList).then((res) => {
|
save(optionList).then((res) => {
|
||||||
// siteConfigStore().save(data.form);
|
appStore.save(form.value);
|
||||||
handleCancel();
|
handleCancel();
|
||||||
proxy.$message.success(res.msg);
|
proxy.$message.success(res.msg);
|
||||||
});
|
});
|
||||||
@ -360,6 +362,7 @@
|
|||||||
await resetValue(queryParams.value);
|
await resetValue(queryParams.value);
|
||||||
proxy.$message.success('恢复成功');
|
proxy.$message.success('恢复成功');
|
||||||
await getConfig();
|
await getConfig();
|
||||||
|
appStore.save(form.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -37,6 +37,7 @@ import org.springframework.validation.annotation.Validated;
|
|||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.annotation.SaIgnore;
|
||||||
import cn.hutool.core.lang.tree.Tree;
|
import cn.hutool.core.lang.tree.Tree;
|
||||||
import cn.hutool.core.util.ClassUtil;
|
import cn.hutool.core.util.ClassUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
@ -54,6 +55,7 @@ import top.charles7c.cnadmin.common.util.validate.ValidationUtils;
|
|||||||
import top.charles7c.cnadmin.monitor.annotation.Log;
|
import top.charles7c.cnadmin.monitor.annotation.Log;
|
||||||
import top.charles7c.cnadmin.system.model.query.DeptQuery;
|
import top.charles7c.cnadmin.system.model.query.DeptQuery;
|
||||||
import top.charles7c.cnadmin.system.model.query.MenuQuery;
|
import top.charles7c.cnadmin.system.model.query.MenuQuery;
|
||||||
|
import top.charles7c.cnadmin.system.model.query.OptionQuery;
|
||||||
import top.charles7c.cnadmin.system.model.query.RoleQuery;
|
import top.charles7c.cnadmin.system.model.query.RoleQuery;
|
||||||
import top.charles7c.cnadmin.system.model.vo.RoleVO;
|
import top.charles7c.cnadmin.system.model.vo.RoleVO;
|
||||||
import top.charles7c.cnadmin.system.service.*;
|
import top.charles7c.cnadmin.system.service.*;
|
||||||
@ -78,6 +80,7 @@ public class CommonController {
|
|||||||
private final DictItemService dictItemService;
|
private final DictItemService dictItemService;
|
||||||
private final ProjectProperties projectProperties;
|
private final ProjectProperties projectProperties;
|
||||||
private final LocalStorageProperties localStorageProperties;
|
private final LocalStorageProperties localStorageProperties;
|
||||||
|
private final OptionService optionService;
|
||||||
|
|
||||||
@Operation(summary = "上传文件", description = "上传文件")
|
@Operation(summary = "上传文件", description = "上传文件")
|
||||||
@PostMapping("/file")
|
@PostMapping("/file")
|
||||||
@ -123,6 +126,14 @@ public class CommonController {
|
|||||||
return enumClass.map(this::listEnumDict).orElseGet(() -> R.ok(dictItemService.listByDictCode(code)));
|
return enumClass.map(this::listEnumDict).orElseGet(() -> R.ok(dictItemService.listByDictCode(code)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SaIgnore
|
||||||
|
@Operation(summary = "查询参数", description = "查询参数")
|
||||||
|
@GetMapping("/option")
|
||||||
|
public R<List<LabelValueVO>> listOption(@Validated OptionQuery query) {
|
||||||
|
return R.ok(optionService.list(query).stream().map(option -> new LabelValueVO(option.getCode(),
|
||||||
|
StrUtil.nullToDefault(option.getValue(), option.getDefaultValue()))).collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据枚举类名查询
|
* 根据枚举类名查询
|
||||||
*
|
*
|
||||||
@ -140,7 +151,7 @@ public class CommonController {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询枚举字典
|
* 查询枚举字典
|
||||||
*
|
*
|
||||||
* @param enumClass
|
* @param enumClass
|
||||||
* 枚举类型
|
* 枚举类型
|
||||||
* @return 枚举字典
|
* @return 枚举字典
|
||||||
|
Loading…
Reference in New Issue
Block a user