Files
fe.ems.vue3/src/views/system/setting/components/change-logo.vue

354 lines
10 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script lang="ts" setup>
import { Modal, message } from 'ant-design-vue/lib';
import { FileType } from 'ant-design-vue/lib/upload/interface';
import { UploadRequestOption } from 'ant-design-vue/lib/vc-upload/interface';
import IconFont from '@/components/IconFont/index.vue';
import { onMounted, reactive, watch, computed } from 'vue';
import useAppStore from '@/store/modules/app';
import useI18n from '@/hooks/useI18n';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { uploadFile } from '@/api/tool/file';
import { changeValue } from '@/api/system/config';
import { sessionGet } from '@/utils/cache-session-utils';
import { transferStaticFile } from '@/api';
import { parseUrlPath } from '@/plugins/file-static-url';
const appStore = useAppStore();
const { t, currentLocale, optionsLocale } = useI18n();
type StateType = {
edite: boolean;
loading: boolean;
language: string;
filePath: string; // 是否上传文件
flag: string; // 是否变更标记
type: 'brand' | 'icon';
icon: string;
brand: string;
};
let state: StateType = reactive({
edite: false,
loading: false,
language: '',
filePath: '',
flag: '',
type: 'icon',
icon: '',
brand: '',
});
/**上传前检查或转换压缩 */
function fnBeforeUpload(file: FileType) {
if (state.loading) return false;
const isJpgOrPng = ['image/jpeg', 'image/png'].includes(file.type);
if (!isJpgOrPng) {
message.error(
t('views.system.setting.uploadFormat', { format: 'jpg、png' }),
3
);
}
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
message.error(t('views.system.setting.uploadSize', { size: 2 }), 3);
}
return isJpgOrPng && isLt2M;
}
/**上传变更 */
function fnUpload(up: UploadRequestOption) {
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.system.setting.sysLogoTipContentUpload'),
onOk() {
// 发送请求
const hide = message.loading(t('common.loading'), 0);
state.loading = true;
let formData = new FormData();
formData.append('file', up.file);
formData.append('subPath', 'default');
uploadFile(formData).then(res => {
state.loading = false;
hide();
if (res.code === RESULT_CODE_SUCCESS) {
message.success(t('views.system.setting.uploadSuccess'), 3);
state.filePath = res.data.fileName;
// 兼容旧前端可改配置文件
const baseUrl = import.meta.env.PROD
? sessionGet('baseUrl') || import.meta.env.VITE_API_BASE_URL
: import.meta.env.VITE_API_BASE_URL;
if (state.type === 'icon') {
state.icon = `${baseUrl}${res.data.fileName}`;
}
if (state.type === 'brand') {
state.brand = `${baseUrl}${res.data.fileName}`;
}
} else {
message.error(res.msg, 3);
}
});
},
});
}
/**进入可编辑 */
function fnEdit(v: boolean) {
state.edite = v;
if (!v) {
Object.assign(state, {
filePath: '',
flag: `${appStore.logoType}/`,
type: appStore.logoType,
icon: getLogoURL('icon'),
brand: getLogoURL('brand'),
});
}
}
/**提交保存 */
function fnSave() {
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.system.setting.sysLogoTipContent'),
onOk() {
const reqArr = [];
// 改变LOGO地址
if (state.filePath) {
let changeFilePath = appStore.filePathIcon;
if (state.type === 'brand') {
changeFilePath = appStore.filePathBrand;
}
reqArr.push(
transferStaticFile({
language: state.language,
uploadPath: state.filePath,
staticPath: changeFilePath,
})
);
}
// 判断类型是否改变
if (state.type !== appStore.logoType) {
reqArr.push(changeValue({ key: 'sys.logo.type', value: state.type }));
}
// 发送请求
const hide = message.loading(t('common.loading'), 0);
state.loading = true;
Promise.all(reqArr).then(resArr => {
state.loading = false;
hide();
if (resArr[0].code === RESULT_CODE_SUCCESS) {
message.success(t('views.system.setting.saveSuccess'), 3);
if (state.type !== appStore.logoType) {
appStore.logoType = state.type;
}
fnEdit(false);
} else {
message.error(resArr[0].msg, 3);
}
});
},
});
}
/**有变更状态进行禁用提交按钮 */
const changeStatus = computed(() => {
const changeFlag = `${state.type}/${state.filePath}`;
if (changeFlag !== state.flag) {
return false;
}
return true;
});
// LOGO地址
function getLogoURL(type: 'brand' | 'icon') {
let url =
type === 'brand'
? parseUrlPath(appStore.filePathBrand)
: parseUrlPath(appStore.filePathIcon);
if (url.indexOf('{language}') === -1) {
return url;
}
// 语言参数替换
const local = state.language;
const lang = local.split('_')[0];
return url.replace('{language}', lang || 'en');
}
// 监听语言切换
watch(
() => state.language,
() => {
state.icon = getLogoURL('icon');
state.brand = getLogoURL('brand');
}
);
onMounted(() => {
Object.assign(state, {
language: currentLocale.value,
filePath: '',
flag: `${appStore.logoType}/`,
type: appStore.logoType,
icon: getLogoURL('icon'),
brand: getLogoURL('brand'),
});
});
</script>
<template>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24" style="margin-bottom: 30px">
<a-form layout="vertical">
<a-form-item style="margin-bottom: 12px">
<div class="header">
<div class="header-brand" v-show="state.type === 'brand'">
<img :width="174" :height="48" :src="state.brand" />
</div>
<div class="header-icon" v-show="state.type === 'icon'">
<img :src="state.icon" />
<h1 :title="appStore.appName">
{{ appStore.appName }}
</h1>
</div>
<div class="header-menu">
<IconFont
type="icon-pcduan"
style="margin-right: 10px"
></IconFont>
{{ t('router.index') }}
</div>
</div>
</a-form-item>
<a-form-item>
<a-select v-model:value="state.language" style="width: 100px">
<a-select-option
v-for="opt in optionsLocale"
:key="opt.value"
:value="opt.value"
>
{{ opt.label }}
</a-select-option>
</a-select>
</a-form-item>
</a-form>
<a-form layout="vertical" v-if="state.edite">
<a-form-item>
<a-space direction="horizontal" :size="18">
<a-radio-group v-model:value="state.type" button-style="solid">
<a-radio value="brand">
{{ t('views.system.setting.sysLogoBrand') }}
</a-radio>
<a-radio value="icon">
{{ t('views.system.setting.sysLogoIcon') }}
</a-radio>
</a-radio-group>
<a-upload
name="file"
list-type="picture"
accept=".jpg,.png"
:max-count="1"
:show-upload-list="false"
:before-upload="fnBeforeUpload"
:custom-request="fnUpload"
>
<a-button type="link" :loading="state.loading">
{{ t('views.system.setting.sysLogoUpload') }}
</a-button>
</a-upload>
</a-space>
</a-form-item>
<a-form-item>
<a-button type="primary" :disabled="changeStatus" @click="fnSave">
{{ t('views.system.setting.saveSubmit') }}
</a-button>
<a-button style="margin-left: 10px" @click="fnEdit(false)">
{{ t('common.cancel') }}
</a-button>
</a-form-item>
</a-form>
<a-button type="dashed" @click="fnEdit(true)" v-else>
{{ t('common.editText') }}
</a-button>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-typography>
<a-typography-paragraph>
{{ t('views.system.setting.sysLogoInstruction') }}<br />
{{ t('views.system.setting.sysLogoInstruction1') }}
</a-typography-paragraph>
<a-typography-title :level="5">{{
t('views.system.setting.sysLogoBrand')
}}</a-typography-title>
<a-typography-paragraph>
{{ t('views.system.setting.sysLogoInstruction2') }}
<a-typography-text mark>174x48</a-typography-text>
</a-typography-paragraph>
<a-typography-title :level="5">
{{ t('views.system.setting.sysLogoIcon') }}
</a-typography-title>
<a-typography-paragraph>
{{ t('views.system.setting.sysLogoInstruction3') }}<br />
{{ t('views.system.setting.sysLogoInstruction4') }}
<a-typography-text mark>1:1</a-typography-text>
eg<a-typography-text mark>132x132</a-typography-text>
</a-typography-paragraph>
</a-typography>
</a-col>
</a-row>
</template>
<style lang="less" scoped>
.header {
display: flex;
align-content: center;
height: 48px;
padding-left: 16px;
background-color: #001529;
&-brand {
width: 174px;
height: 48px;
margin-right: 12px;
border: 1px var(--ant-primary-color) solid;
}
&-icon {
display: flex;
align-content: center;
min-width: 174px;
height: 100%;
align-items: center;
margin-right: 16px;
border: 1px var(--ant-primary-color) solid;
& > img {
height: 32px;
width: 32px;
vertical-align: middle;
border-style: none;
border-radius: 6.66px;
}
& > h1 {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 130px;
color: #fff;
margin: 0 0 0 12px;
font-weight: 600;
font-size: 16px;
}
}
&-menu {
width: 90px;
line-height: 40px;
color: #ffffff;
align-self: center;
text-align: center;
}
}
</style>