commit
6013211a09
@ -3,6 +3,7 @@ import qs from 'query-string';
|
||||
import { ListParam as DeptParam } from '@/api/system/dept';
|
||||
import { ListParam as MenuParam } from '@/api/system/menu';
|
||||
import { ListParam as RoleParam } from '@/api/system/role';
|
||||
import { ListParam as OptionParam } from '@/api/system/config';
|
||||
import { TreeNodeData } from '@arco-design/web-vue';
|
||||
import { LabelValueState } from '@/store/modules/dict/types';
|
||||
|
||||
@ -39,6 +40,15 @@ export function listDict(code: string) {
|
||||
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) {
|
||||
return axios.post(`${BASE_URL}/file`, data);
|
||||
}
|
||||
|
@ -1,28 +1,14 @@
|
||||
<template>
|
||||
<a-layout-footer class="footer">
|
||||
{{ `Copyright © 2022-${new Date().getFullYear()}` }}
|
||||
<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>
|
||||
<div v-html="appStore.getCopyright"></div>
|
||||
</a-layout-footer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup></script>
|
||||
<script lang="ts" setup>
|
||||
import { useAppStore } from '@/store';
|
||||
|
||||
const appStore = useAppStore();
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.footer {
|
||||
@ -33,13 +19,4 @@
|
||||
color: var(--color-text-2);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: var(--color-text-2);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: rgb(var(--gray-6));
|
||||
}
|
||||
</style>
|
||||
|
@ -2,12 +2,12 @@
|
||||
<div class="navbar">
|
||||
<div class="left-side">
|
||||
<a-space>
|
||||
<img alt="logo" src="/logo.svg" />
|
||||
<img alt="logo" :src="getFile(appStore.getLogo)" height="33"/>
|
||||
<a-typography-title
|
||||
:style="{ margin: 0, fontSize: '18px' }"
|
||||
:heading="5"
|
||||
>
|
||||
{{ $t('title') }}
|
||||
{{ appStore.getTitle }}
|
||||
</a-typography-title>
|
||||
<icon-menu-fold
|
||||
v-if="!topMenu && appStore.device === 'mobile'"
|
||||
@ -198,6 +198,7 @@
|
||||
import useUser from '@/hooks/user';
|
||||
import Menu from '@/components/menu/index.vue';
|
||||
import getAvatar from '@/utils/avatar';
|
||||
import getFile from '@/utils/file';
|
||||
import MessageBox from '../message-box/index.vue';
|
||||
|
||||
const appStore = useAppStore();
|
||||
|
@ -1,13 +1,15 @@
|
||||
import type { Router, LocationQueryRaw } from 'vue-router';
|
||||
import NProgress from 'nprogress'; // progress bar
|
||||
|
||||
import { useLoginStore } from '@/store';
|
||||
import { useLoginStore, useAppStore } from '@/store';
|
||||
import { isLogin } from '@/utils/auth';
|
||||
|
||||
export default function setupUserLoginInfoGuard(router: Router) {
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
NProgress.start();
|
||||
const loginStore = useLoginStore();
|
||||
const appStore = useAppStore();
|
||||
appStore.init();
|
||||
if (isLogin()) {
|
||||
if (loginStore.roles[0]) {
|
||||
next();
|
||||
|
@ -9,7 +9,9 @@ import type { MessageReturn } from '@arco-design/web-vue/es/message/interface';
|
||||
import type { RouteRecordNormalized } from 'vue-router';
|
||||
import defaultSettings from '@/config/settings.json';
|
||||
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 = (
|
||||
appMenu: RouteRecordNormalized[],
|
||||
@ -45,6 +47,18 @@ const useAppStore = defineStore('app', {
|
||||
);
|
||||
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: {
|
||||
@ -97,6 +111,51 @@ const useAppStore = defineStore('app', {
|
||||
clearServerMenu() {
|
||||
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';
|
||||
|
||||
export interface Config {
|
||||
site_title?: string;
|
||||
site_copyright?: string;
|
||||
site_logo?: string;
|
||||
site_favicon?: string;
|
||||
}
|
||||
export interface AppState {
|
||||
theme: string;
|
||||
colorWeak: boolean;
|
||||
@ -18,4 +24,5 @@ export interface AppState {
|
||||
menuFromServer: boolean;
|
||||
serverMenu: RouteRecordNormalized[];
|
||||
[key: string]: unknown;
|
||||
config?: Config;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<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>
|
||||
<a-form
|
||||
ref="formRef"
|
||||
@ -71,7 +71,7 @@
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useStorage } from '@vueuse/core';
|
||||
import { useLoginStore } from '@/store';
|
||||
import { useLoginStore, useAppStore } from '@/store';
|
||||
import { encryptByRsa } from '@/utils/encrypt';
|
||||
// import debug from '@/utils/env';
|
||||
|
||||
@ -79,6 +79,7 @@
|
||||
|
||||
const captchaImgBase64 = ref('');
|
||||
const loginStore = useLoginStore();
|
||||
const appStore = useAppStore();
|
||||
const loading = ref(false);
|
||||
const { t } = useI18n();
|
||||
const router = useRouter();
|
||||
|
@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="logo">
|
||||
<img src="/logo.svg" alt="logo" />
|
||||
<div class="logo-text">{{ $t('title') }}</div>
|
||||
<img :src="getFile(appStore.getLogo)" alt="logo" height="33" />
|
||||
<div class="logo-text">{{ appStore.getTitle }}</div>
|
||||
</div>
|
||||
<LoginBanner />
|
||||
<div class="content">
|
||||
@ -18,8 +18,12 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import Footer from '@/components/footer/index.vue';
|
||||
import { useAppStore } from '@/store';
|
||||
import getFile from '@/utils/file';
|
||||
import LoginBanner from './components/banner.vue';
|
||||
import LoginForm from './components/login-form.vue';
|
||||
|
||||
const appStore = useAppStore();
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
@ -170,6 +170,7 @@
|
||||
} from '@/api/system/config';
|
||||
import { upload } from '@/api/common';
|
||||
import getFile from '@/utils/file';
|
||||
import { useAppStore } from '@/store';
|
||||
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
const dataList = ref<DataRecord[]>([]);
|
||||
@ -180,6 +181,7 @@
|
||||
const siteCopyright = ref<DataRecord>();
|
||||
const siteLogo = ref<DataRecord>();
|
||||
const siteFavicon = ref<DataRecord>();
|
||||
const appStore = useAppStore();
|
||||
|
||||
const data = reactive({
|
||||
queryParams: {
|
||||
@ -251,7 +253,7 @@
|
||||
}
|
||||
);
|
||||
save(optionList).then((res) => {
|
||||
// siteConfigStore().save(data.form);
|
||||
appStore.save(form.value);
|
||||
handleCancel();
|
||||
proxy.$message.success(res.msg);
|
||||
});
|
||||
@ -360,6 +362,7 @@
|
||||
await resetValue(queryParams.value);
|
||||
proxy.$message.success('恢复成功');
|
||||
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.multipart.MultipartFile;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaIgnore;
|
||||
import cn.hutool.core.lang.tree.Tree;
|
||||
import cn.hutool.core.util.ClassUtil;
|
||||
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.system.model.query.DeptQuery;
|
||||
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.vo.RoleVO;
|
||||
import top.charles7c.cnadmin.system.service.*;
|
||||
@ -78,6 +80,7 @@ public class CommonController {
|
||||
private final DictItemService dictItemService;
|
||||
private final ProjectProperties projectProperties;
|
||||
private final LocalStorageProperties localStorageProperties;
|
||||
private final OptionService optionService;
|
||||
|
||||
@Operation(summary = "上传文件", description = "上传文件")
|
||||
@PostMapping("/file")
|
||||
@ -123,6 +126,14 @@ public class CommonController {
|
||||
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
|
||||
* 枚举类型
|
||||
* @return 枚举字典
|
||||
|
Loading…
Reference in New Issue
Block a user