fix: 修复部分缺陷

This commit is contained in:
bunny 2025-04-29 17:50:40 +08:00
parent ba36f0966b
commit 67d30ed976
16 changed files with 126 additions and 87 deletions

View File

@ -1,6 +1,6 @@
{
"name": "bunny-auth-admin",
"version": "3.0.0",
"version": "4.0.0",
"private": true,
"type": "module",
"license": "MIT",

View File

@ -21,9 +21,7 @@ const i18nStore = userI18nStore();
const i18n = useI18n();
const { $storage } = useNav();
/**
* * 设置多语言内容
*/
/* 设置多语言内容 */
const setI18n = async () => {
await i18nStore.loadI18nMap();
const languageData = JSON.parse(localStorage.getItem('i18nStore') as any);
@ -44,9 +42,7 @@ const setI18n = async () => {
i18n.mergeLocaleMessage(locale, languageData.i18n[locale]);
};
/**
* * 当前语言类别
*/
/* 当前语言类别 */
const currentLocale = computed(() => {
const languageData = JSON.parse(localStorage.getItem('i18nStore') as any);
const local = languageData ? languageData.i18n.local : {};

View File

@ -1,4 +1,4 @@
import { computed, defineComponent, onMounted, reactive, unref, watch } from 'vue';
import { watch, unref, computed, reactive, onMounted, defineComponent } from 'vue';
import { countToProps } from './props';
import { isNumber } from '@pureadmin/utils';

View File

@ -1,5 +1,5 @@
import './rebound.css';
import { defineComponent, onBeforeMount, onBeforeUnmount, ref, unref } from 'vue';
import { ref, unref, onBeforeMount, defineComponent, onBeforeUnmount } from 'vue';
import { reboundProps } from './props';
export default defineComponent({

View File

@ -1,12 +1,12 @@
.scroll-num {
animation: enhance-bounce-in-down 1s calc(var(--delay) * 1s) forwards;
width: var(--width, 20px);
height: var(--height, calc(var(--width, 20px) * 1.8));
color: var(--color, #333);
font-size: var(--height, calc(var(--width, 20px) * 1.1));
height: var(--height, calc(var(--width, 20px) * 1.8));
line-height: var(--height, calc(var(--width, 20px) * 1.8));
overflow: hidden;
text-align: center;
width: var(--width, 20px);
overflow: hidden;
animation: enhance-bounce-in-down 1s calc(var(--delay) * 1s) forwards;
}
ul {

View File

@ -2,26 +2,26 @@ import './circled.css';
import Cropper from 'cropperjs';
import { ElUpload } from 'element-plus';
import type { CSSProperties } from 'vue';
import { computed, defineComponent, onMounted, onUnmounted, type PropType, ref, unref } from 'vue';
import { useEventListener } from '@vueuse/core';
import { longpress } from '@/directives/longpress';
import { directive as tippy, useTippy } from 'vue-tippy';
import { debounce, delay, downloadByBase64, isArray, useResizeObserver } from '@pureadmin/utils';
import { useTippy, directive as tippy } from 'vue-tippy';
import { type PropType, ref, unref, computed, onMounted, onUnmounted, defineComponent } from 'vue';
import { delay, debounce, isArray, downloadByBase64, useResizeObserver } from '@pureadmin/utils';
import {
ArrowDown,
ArrowH,
ArrowLeft,
ArrowRight,
ArrowUp,
ArrowV,
ChangeIcon,
DownloadIcon,
Reload,
Upload,
ArrowH,
ArrowV,
ArrowUp,
ArrowDown,
ArrowLeft,
ChangeIcon,
ArrowRight,
RotateLeft,
SearchPlus,
RotateRight,
SearchMinus,
SearchPlus,
Upload,
DownloadIcon,
} from './svg';
type Options = Cropper.Options;
@ -101,7 +101,7 @@ export default defineComponent({
});
const iconClass = computed(() => {
return ['p-[6px]', 'h-[30px]', 'w-[30px]', 'outline-none', 'rounded-[4px]', 'cursor-pointer', 'hover:bg-[rgba(0,0,0,0.06)]'];
return ['p-[6px]', 'h-[30px]', 'w-[30px]', 'outline-hidden', 'rounded-[4px]', 'cursor-pointer', 'hover:bg-[rgba(0,0,0,0.06)]'];
});
const getWrapperStyle = computed((): CSSProperties => {
@ -124,7 +124,7 @@ export default defineComponent({
async function init() {
const imgEl = unref(imgElRef);
if (!imgEl) return;
const result: any = new Cropper(imgEl, {
cropper.value = new Cropper(imgEl, {
...defaultOptions,
ready: () => {
isReady.value = true;
@ -142,11 +142,6 @@ export default defineComponent({
},
...props.options,
});
// 如果图片不存在直接将加载变为加载完成
if (!result.ready) emit('readied', cropper.value);
cropper.value = result;
}
function realTimeCroppered() {
@ -226,30 +221,55 @@ export default defineComponent({
return () => (
<div class="flex flex-wrap w-[60px] justify-between">
<ElUpload accept="image/*" show-file-list={false} before-upload={beforeUpload}>
<Upload class={iconClass.value} v-tippy={{ content: '上传', placement: 'left-start' }} />
<Upload
class={iconClass.value}
v-tippy={{
content: '上传',
placement: 'left-start',
}}
/>
</ElUpload>
<DownloadIcon
class={iconClass.value}
v-tippy={{ content: '下载', placement: 'right-start' }}
v-tippy={{
content: '下载',
placement: 'right-start',
}}
onClick={() => downloadByBase64(imgBase64.value, 'cropping.png')}
/>
<ChangeIcon
class={iconClass.value}
v-tippy={{ content: '圆形、矩形裁剪', placement: 'left-start' }}
v-tippy={{
content: '圆形、矩形裁剪',
placement: 'left-start',
}}
onClick={() => {
inCircled.value = !inCircled.value;
realTimeCroppered();
}}
/>
<Reload class={iconClass.value} v-tippy={{ content: '重置', placement: 'right-start' }} onClick={() => handCropper('reset')} />
<Reload
class={iconClass.value}
v-tippy={{
content: '重置',
placement: 'right-start',
}}
onClick={() => handCropper('reset')}
/>
<ArrowUp
class={iconClass.value}
v-tippy={{ content: '上移(可长按)', placement: 'left-start' }}
v-tippy={{
content: '上移(可长按)',
placement: 'left-start',
}}
v-longpress={[() => handCropper('move', [0, -10]), '0:100']}
/>
<ArrowDown
class={iconClass.value}
v-tippy={{ content: '下移(可长按)', placement: 'right-start' }}
v-tippy={{
content: '下移(可长按)',
placement: 'right-start',
}}
v-longpress={[() => handCropper('move', [0, 10]), '0:100']}
/>
<ArrowLeft
@ -262,21 +282,58 @@ export default defineComponent({
/>
<ArrowRight
class={iconClass.value}
v-tippy={{ content: '右移(可长按)', placement: 'right-start' }}
v-tippy={{
content: '右移(可长按)',
placement: 'right-start',
}}
v-longpress={[() => handCropper('move', [10, 0]), '0:100']}
/>
<ArrowH class={iconClass.value} v-tippy={{ content: '水平翻转', placement: 'left-start' }} onClick={() => handCropper('scaleX', -1)} />
<ArrowV class={iconClass.value} v-tippy={{ content: '垂直翻转', placement: 'right-start' }} onClick={() => handCropper('scaleY', -1)} />
<RotateLeft class={iconClass.value} v-tippy={{ content: '逆时针旋转', placement: 'left-start' }} onClick={() => handCropper('rotate', -45)} />
<RotateRight class={iconClass.value} v-tippy={{ content: '顺时针旋转', placement: 'right-start' }} onClick={() => handCropper('rotate', 45)} />
<ArrowH
class={iconClass.value}
v-tippy={{
content: '水平翻转',
placement: 'left-start',
}}
onClick={() => handCropper('scaleX', -1)}
/>
<ArrowV
class={iconClass.value}
v-tippy={{
content: '垂直翻转',
placement: 'right-start',
}}
onClick={() => handCropper('scaleY', -1)}
/>
<RotateLeft
class={iconClass.value}
v-tippy={{
content: '逆时针旋转',
placement: 'left-start',
}}
onClick={() => handCropper('rotate', -45)}
/>
<RotateRight
class={iconClass.value}
v-tippy={{
content: '顺时针旋转',
placement: 'right-start',
}}
onClick={() => handCropper('rotate', 45)}
/>
<SearchPlus
class={iconClass.value}
v-tippy={{ content: '放大(可长按)', placement: 'left-start' }}
v-tippy={{
content: '放大(可长按)',
placement: 'left-start',
}}
v-longpress={[() => handCropper('zoom', 0.1), '0:100']}
/>
<SearchMinus
class={iconClass.value}
v-tippy={{ content: '缩小(可长按)', placement: 'right-start' }}
v-tippy={{
content: '缩小(可长按)',
placement: 'right-start',
}}
v-longpress={[() => handCropper('zoom', -0.1), '0:100']}
/>
</div>

View File

@ -1,8 +1,11 @@
<script lang="tsx" setup>
<script setup lang="tsx">
import { ref } from 'vue';
import ReCropper from '@/components/ReCropper';
import { formatBytes } from '@pureadmin/utils';
import { $t } from '@/plugins/i18n';
defineOptions({
name: 'ReCropperPreview',
});
defineProps({
imgSrc: String,
@ -31,30 +34,20 @@ defineExpose({ hidePopover });
<template>
<div v-loading="!showPopover" element-loading-background="transparent">
<el-popover ref="popoverRef" :visible="showPopover" placement="right" popper-style="top:260px" width="18vw">
<el-popover ref="popoverRef" :visible="showPopover" placement="right" width="18vw">
<template #reference>
<div class="w-[18vw]">
<ReCropper ref="refCropper" :src="imgSrc" circled @cropper="onCropper" @readied="showPopover = true" />
<p v-show="showPopover" class="mt-1 text-center">{{ $t('cropper_preview_tips') }}</p>
<p v-show="showPopover" class="mt-1 text-center">温馨提示右键上方裁剪区可开启功能菜单</p>
</div>
</template>
<div class="flex flex-wrap justify-center items-center text-center">
<el-image v-if="cropperImg" :preview-src-list="Array.of(cropperImg)" :src="cropperImg" class="cropper-img-preview" fit="contain" />
<el-image v-if="cropperImg" :src="cropperImg" :preview-src-list="Array.of(cropperImg)" fit="cover" />
<div v-if="infos" class="mt-1">
<p>{{ $t('image_size') }}{{ parseInt(infos.width) }} × {{ parseInt(infos.height) }}{{ $t('pixel') }}</p>
<p>{{ $t('file_size') }}{{ formatBytes(infos.size) }}{{ infos.size }} {{ $t('bytes') }}</p>
<p>图像大小{{ parseInt(infos.width) }} × {{ parseInt(infos.height) }}像素</p>
<p>文件大小{{ formatBytes(infos.size) }}{{ infos.size }} 字节</p>
</div>
</div>
</el-popover>
</div>
</template>
<style lang="scss" scoped>
.cropper-img-preview {
height: 200px;
:deep(.el-image__inner) {
max-height: 310px;
}
}
</style>

View File

@ -16,7 +16,7 @@ defineProps({
</script>
<template>
<div v-if="list.length">
<div v-if="list.length" class="mb-4">
<NoticeItem v-for="(item, index) in list" :key="index" :noticeItem="item" />
</div>
<el-empty v-else :description="emptyText" />

View File

@ -10,21 +10,22 @@ import { useTranslationLang } from '../../hooks/useTranslationLang';
import { usePermissionStoreHook } from '@/store/permission';
import LaySidebarItem from '../lay-sidebar/components/SidebarItem.vue';
import LaySidebarFullScreen from '../lay-sidebar/components/SidebarFullScreen.vue';
import GlobalizationIcon from '@/assets/svg/globalization.svg?component';
import LogoutCircleRLine from '@iconify-icons/ri/logout-circle-r-line';
import Setting from '@iconify-icons/ri/settings-3-line';
import Check from '@iconify-icons/ep/check';
import { $t } from '@/plugins/i18n';
import { userI18nTypeStore } from '@/store/i18n/i18nType';
import GlobalizationIcon from '@/assets/svg/globalization.svg?component';
const menuRef = ref();
const showLogo = ref(storageLocal().getItem<StorageConfigs>(`${responsiveStorageNameSpace()}configure`)?.showLogo ?? true);
const { t, route, locale, translationCh, translationEn } = useTranslationLang(menuRef);
const { t, route, locale, translation } = useTranslationLang(menuRef);
const { title, logout, onPanel, getLogo, username, userAvatar, backTopMenu, avatarsStyle, getDropdownItemStyle, getDropdownItemClass } = useNav();
const defaultActive = computed(() => (!isAllEmpty(route.meta?.activePath) ? route.meta.activePath : route.path));
const i18nTypeStore = userI18nTypeStore();
nextTick(() => {
menuRef.value?.handleResize();
});
@ -49,29 +50,21 @@ onMounted(() => {
<!-- 菜单搜索 -->
<LaySearch id="header-search" />
<!-- 国际化 -->
<el-dropdown id="header-translation" trigger="click">
<GlobalizationIcon class="navbar-bg-hover w-[40px] h-[48px] p-[11px] cursor-pointer outline-none" />
<el-dropdown trigger="click">
<GlobalizationIcon class="hover:text-primary hover:!bg-[transparent] w-[20px] h-[20px] ml-1.5 cursor-pointer outline-none duration-300" />
<template #dropdown>
<el-dropdown-menu class="translation">
<el-dropdown-item
:class="['dark:!text-white', getDropdownItemClass(locale, 'zh')]"
:style="getDropdownItemStyle(locale, 'zh')"
@click="translationCh"
v-for="item in i18nTypeStore.translationTypeList"
:key="item.key"
:class="['dark:!text-white', getDropdownItemClass(locale, item.key)]"
:style="getDropdownItemStyle(locale, item.key)"
@click="translation(item.key)"
>
<span v-show="locale === 'zh'" class="check-zh">
<span v-show="locale === item.key" class="check">
<IconifyIconOffline :icon="Check" />
</span>
简体中文
</el-dropdown-item>
<el-dropdown-item
:class="['dark:!text-white', getDropdownItemClass(locale, 'en')]"
:style="getDropdownItemStyle(locale, 'en')"
@click="translationEn"
>
<span v-show="locale === 'en'" class="check-en">
<IconifyIconOffline :icon="Check" />
</span>
English
{{ item.value }}
</el-dropdown-item>
</el-dropdown-menu>
</template>

View File

@ -1,5 +1,5 @@
<script lang="ts" setup>
import ReCropperPreview from '@/components/CropperPreview';
import ReCropperPreview from '@/components/ReCropperPreview';
import { sexConstant } from '@/enums/baseConstant';
import { $t } from '@/plugins/i18n';
import { useAdminUserStore } from '@/store/system/adminUser';

View File

@ -8,7 +8,7 @@ import { $t } from '@/plugins/i18n';
import { isAddUserinfo } from '@/views/system/admin-user/utils/columns';
import ResetPasswordDialog from '@/components/Table/ResetPasswords.vue';
import { deviceDetection, handleTree } from '@pureadmin/utils';
import CropperPreview from '@/components/CropperPreview';
import CropperPreview from '@/components/ReCropperPreview';
import AssignUserToRole from '@/views/system/admin-user/components/assign-roles-to-user.vue';
import { useUserStore } from '@/store/system/user';
import { useDeptStore } from '@/store/system/dept';

View File

@ -2,7 +2,7 @@
import { onMounted, ref } from 'vue';
import ReCol from '@/components/ReCol';
import { chartData, useDark } from './utils';
import { ReNormalCountTo } from '@/components/CountTo';
import { ReNormalCountTo } from '@/components/ReCountTo';
import ChartLine from '@/views/welcome/components/ChartLine.vue';
import ChartRound from '@/views/welcome/components/ChartRound.vue';
import { getServerCommitList, getWebCommitList, serverCommitList, webCommitList } from '@/views/welcome/utils';