mirror of
				https://github.com/continew-org/continew-admin.git
				synced 2025-11-04 10:57:10 +08:00 
			
		
		
		
	@@ -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()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 根据枚举类名查询
 | 
			
		||||
     *
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user