feat(setting): 系统设置支持上传 App 登录页背景
This commit is contained in:
1 parent
a94018d5d3
commit
dd4be08d49
2 files changed
+138
-3
No files matched your search
@@ -46,6 +46,13 @@ export const BANNER_IMAGE: ImagePreset = {
|
|||||||
maxHeight: 1164
|
maxHeight: 1164
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const LOGIN_BACKGROUND: ImagePreset = {
|
||||||
|
label: '登录页背景',
|
||||||
|
hint: '建议 1170×2532(390×844dp × 3,iPhone 整屏竖图),min 720×1560',
|
||||||
|
maxWidth: 1170,
|
||||||
|
maxHeight: 2532
|
||||||
|
}
|
||||||
|
|
||||||
/** 按规格压缩:尺寸未超且体积不大时原样返回,不做二次编码。 */
|
/** 按规格压缩:尺寸未超且体积不大时原样返回,不做二次编码。 */
|
||||||
export function compressForPreset(file: File, preset: ImagePreset): Promise<File> {
|
export function compressForPreset(file: File, preset: ImagePreset): Promise<File> {
|
||||||
return compressImage(file, {
|
return compressImage(file, {
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
<!-- Author: fruit.com.cn -->
|
<!-- Author: fruit.com.cn -->
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref } from 'vue'
|
import { computed, onMounted, ref } from 'vue'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { listConfigsApi, updateConfigsApi } from '@/api/admin/config'
|
import { listConfigsApi, updateConfigsApi } from '@/api/admin/config'
|
||||||
|
import { uploadFile } from '@/utils/uploader'
|
||||||
|
import { compressForPreset, LOGIN_BACKGROUND } from '@/views/contentManagement/upload/imagePresets'
|
||||||
import { useRbac } from '@/stores/rbac'
|
import { useRbac } from '@/stores/rbac'
|
||||||
|
|
||||||
const { can } = useRbac()
|
const { can } = useRbac()
|
||||||
@@ -14,6 +16,15 @@ const workspaceName = ref('')
|
|||||||
const defaultStatus = ref('draft')
|
const defaultStatus = ref('draft')
|
||||||
const registerMaxPerIp = ref(5)
|
const registerMaxPerIp = ref(5)
|
||||||
const registerMaxPerDevice = ref(3)
|
const registerMaxPerDevice = ref(3)
|
||||||
|
const loginBackgroundUrl = ref('')
|
||||||
|
|
||||||
|
const MAX_LOGIN_BG_SIZE = 8 * 1024 * 1024 // 8 MB
|
||||||
|
const ALLOWED_LOGIN_BG_TYPES = ['image/jpeg', 'image/png', 'image/webp']
|
||||||
|
const uploadingBg = ref(false)
|
||||||
|
const bgError = ref('')
|
||||||
|
const bgFileName = ref('')
|
||||||
|
|
||||||
|
const canSave = computed(() => can('system:role'))
|
||||||
|
|
||||||
const statusOptions = [
|
const statusOptions = [
|
||||||
{ code: 'draft', text: '草稿' },
|
{ code: 'draft', text: '草稿' },
|
||||||
@@ -34,6 +45,7 @@ async function load() {
|
|||||||
defaultStatus.value = map['content.default-status'] || 'draft'
|
defaultStatus.value = map['content.default-status'] || 'draft'
|
||||||
registerMaxPerIp.value = parseRegisterLimit(map['app.register.max-per-ip-per-day'], 5)
|
registerMaxPerIp.value = parseRegisterLimit(map['app.register.max-per-ip-per-day'], 5)
|
||||||
registerMaxPerDevice.value = parseRegisterLimit(map['app.register.max-per-device-per-day'], 3)
|
registerMaxPerDevice.value = parseRegisterLimit(map['app.register.max-per-device-per-day'], 3)
|
||||||
|
loginBackgroundUrl.value = map['app.login.background.url'] || ''
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
@@ -46,7 +58,8 @@ async function save() {
|
|||||||
'workspace.name': workspaceName.value,
|
'workspace.name': workspaceName.value,
|
||||||
'content.default-status': defaultStatus.value,
|
'content.default-status': defaultStatus.value,
|
||||||
'app.register.max-per-ip-per-day': String(registerMaxPerIp.value),
|
'app.register.max-per-ip-per-day': String(registerMaxPerIp.value),
|
||||||
'app.register.max-per-device-per-day': String(registerMaxPerDevice.value)
|
'app.register.max-per-device-per-day': String(registerMaxPerDevice.value),
|
||||||
|
'app.login.background.url': loginBackgroundUrl.value
|
||||||
})
|
})
|
||||||
ElMessage.success('设置已保存')
|
ElMessage.success('设置已保存')
|
||||||
} finally {
|
} finally {
|
||||||
@@ -54,6 +67,52 @@ async function save() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function beforeBgUpload(file) {
|
||||||
|
bgError.value = ''
|
||||||
|
if (!ALLOWED_LOGIN_BG_TYPES.includes(file.type)) {
|
||||||
|
bgError.value = '图片仅支持 JPG、PNG 或 WebP 格式'
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (file.size > MAX_LOGIN_BG_SIZE) {
|
||||||
|
bgError.value = '图片大小不能超过 8MB'
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
async function uploadBg(options) {
|
||||||
|
uploadingBg.value = true
|
||||||
|
bgError.value = ''
|
||||||
|
try {
|
||||||
|
const file = await compressForPreset(options.file, LOGIN_BACKGROUND)
|
||||||
|
const asset = await uploadFile(file, {
|
||||||
|
bizType: 'LOGIN_BG'
|
||||||
|
})
|
||||||
|
loginBackgroundUrl.value = asset.url
|
||||||
|
bgFileName.value = file.name
|
||||||
|
ElMessage.success('登录页背景已上传')
|
||||||
|
} catch {
|
||||||
|
bgError.value = '图片上传失败,请检查网络后重试'
|
||||||
|
} finally {
|
||||||
|
uploadingBg.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeBg() {
|
||||||
|
if (uploadingBg.value) return
|
||||||
|
loginBackgroundUrl.value = ''
|
||||||
|
bgFileName.value = ''
|
||||||
|
bgError.value = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const loginBgPreview = computed(() => {
|
||||||
|
const url = loginBackgroundUrl.value
|
||||||
|
if (!url) return ''
|
||||||
|
if (url.startsWith('http://') || url.startsWith('https://')) return url
|
||||||
|
if (url.startsWith('/')) return url
|
||||||
|
return `/${url}`
|
||||||
|
})
|
||||||
|
|
||||||
onMounted(load)
|
onMounted(load)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -78,9 +137,78 @@ onMounted(load)
|
|||||||
><el-input-number v-model="registerMaxPerIp" :step="1" :precision="0" /></el-form-item
|
><el-input-number v-model="registerMaxPerIp" :step="1" :precision="0" /></el-form-item
|
||||||
><el-form-item label="单设备 24 小时注册上限(0 或负数表示不限制)"
|
><el-form-item label="单设备 24 小时注册上限(0 或负数表示不限制)"
|
||||||
><el-input-number v-model="registerMaxPerDevice" :step="1" :precision="0" /></el-form-item
|
><el-input-number v-model="registerMaxPerDevice" :step="1" :precision="0" /></el-form-item
|
||||||
><el-button type="primary" :loading="saving" :disabled="!can('system:role')" @click="save"
|
>
|
||||||
|
<el-divider content-position="left">App 登录页</el-divider>
|
||||||
|
<el-form-item label="登录页背景图">
|
||||||
|
<div class="login-bg-field">
|
||||||
|
<el-upload
|
||||||
|
:show-file-list="false"
|
||||||
|
:before-upload="beforeBgUpload"
|
||||||
|
:http-request="uploadBg"
|
||||||
|
accept=".jpg,.jpeg,.png,.webp"
|
||||||
|
>
|
||||||
|
<el-button :disabled="uploadingBg" :loading="uploadingBg">
|
||||||
|
{{ loginBackgroundUrl ? '重新上传' : '上传背景图' }}
|
||||||
|
</el-button>
|
||||||
|
</el-upload>
|
||||||
|
<span class="field-hint">建议 {{ LOGIN_BACKGROUND.hint }}</span>
|
||||||
|
<span v-if="bgFileName" class="field-filename">当前:{{ bgFileName }}</span>
|
||||||
|
<span v-if="bgError" class="field-error">{{ bgError }}</span>
|
||||||
|
<div v-if="loginBgPreview" class="login-bg-preview">
|
||||||
|
<img :src="loginBgPreview" alt="登录页背景预览" />
|
||||||
|
<el-button
|
||||||
|
class="remove-bg-btn"
|
||||||
|
text
|
||||||
|
type="danger"
|
||||||
|
size="small"
|
||||||
|
@click="removeBg"
|
||||||
|
>移除</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-button type="primary" :loading="saving" :disabled="!canSave" @click="save"
|
||||||
>保存设置</el-button
|
>保存设置</el-button
|
||||||
></el-form
|
></el-form
|
||||||
></section
|
></section
|
||||||
>
|
>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.login-bg-field {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
.field-hint {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
}
|
||||||
|
.field-filename {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--el-text-color-primary);
|
||||||
|
}
|
||||||
|
.field-error {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--el-color-danger);
|
||||||
|
}
|
||||||
|
.login-bg-preview {
|
||||||
|
position: relative;
|
||||||
|
margin-top: 4px;
|
||||||
|
width: 96px;
|
||||||
|
aspect-ratio: 1170 / 2532;
|
||||||
|
border-radius: 6px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: var(--el-fill-color-light);
|
||||||
|
}
|
||||||
|
.login-bg-preview img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
.remove-bg-btn {
|
||||||
|
position: absolute;
|
||||||
|
top: 2px;
|
||||||
|
right: 2px;
|
||||||
|
padding: 2px 4px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in new issue
Block a user