Merge branch 'master' of https://github.com/zouzhibin/vue-admin-perfect into vue-i18n
Conflicts: src/layout/Header/index.vue src/views/home/index.vue
This commit is contained in:
commit
548d6fae3f
Binary file not shown.
|
After Width: | Height: | Size: 5.9 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 100 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 53 KiB |
|
|
@ -0,0 +1,86 @@
|
|||
<template>
|
||||
<vue-cropper
|
||||
ref="cropper"
|
||||
:img="avatarUrl"
|
||||
:output-size="defaultOptions.outputSize"
|
||||
:output-type="defaultOptions.outputType"
|
||||
:info="defaultOptions.info"
|
||||
:full="defaultOptions.full"
|
||||
:fixed="defaultOptions.fixed"
|
||||
:autoCropWidth="defaultOptions.autoCropWidth"
|
||||
:autoCropHeight="defaultOptions.autoCropHeight"
|
||||
:fixed-box="defaultOptions.fixedBox"
|
||||
:auto-crop="defaultOptions.autoCrop"
|
||||
:center-box="defaultOptions.centerBox"
|
||||
@real-time="realTime"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {defineExpose, reactive, ref} from 'vue'
|
||||
import { VueCropper } from 'vue-cropper'
|
||||
import 'vue-cropper/dist/index.css'
|
||||
|
||||
const emit = defineEmits(['change'])
|
||||
let props = defineProps({
|
||||
avatarUrl:{
|
||||
type:String
|
||||
},
|
||||
options:{
|
||||
type:Object,
|
||||
default:()=>{}
|
||||
}
|
||||
})
|
||||
|
||||
let cropper = ref()
|
||||
|
||||
const defaultOptions = reactive({
|
||||
outputSize:0.8, // 裁剪生成图片的质量
|
||||
outputType: 'png', // 生成图片的格式
|
||||
info: true, // 裁剪框的大小信息
|
||||
fixed: true, // 是否开启截图框宽高固定比例
|
||||
autoCrop: true, // 是否默认生成截图框
|
||||
anMoveBox: true, // 截图框能否拖动
|
||||
original: false, // 上传图片按照原始比例渲染
|
||||
autoCropWidth: 300, // 默认生成截图框宽度
|
||||
autoCropHeight: 300, // 默认生成截图框高度
|
||||
// 只有自动截图开启 宽度高度才生效
|
||||
centerBox: false, // 截图框是否被限制在图片里面
|
||||
high: true, // 是否按照dpr设备比例图片
|
||||
fixedBox: false, // 固定截图框大小 不允许改变
|
||||
full: false, // 是否输出原图比例的截图
|
||||
...props.options
|
||||
})
|
||||
|
||||
const getBase64 = ()=>{
|
||||
return new Promise(resolve => {
|
||||
cropper.value.getCropData((data) => {
|
||||
resolve(data)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const rotateLeft = () => {
|
||||
cropper.value.rotateLeft()
|
||||
}
|
||||
const rotateRight = () => {
|
||||
cropper.value.rotateRight()
|
||||
}
|
||||
|
||||
const zoom = (percent) => {
|
||||
cropper.value.changeScale(percent)
|
||||
}
|
||||
|
||||
// 实时预览图片
|
||||
const realTime = (data)=>{
|
||||
emit('change',data)
|
||||
}
|
||||
|
||||
|
||||
defineExpose({
|
||||
getBase64,
|
||||
rotateLeft,
|
||||
rotateRight,
|
||||
zoom
|
||||
})
|
||||
</script>
|
||||
|
|
@ -1,16 +1,26 @@
|
|||
<template>
|
||||
<el-switch @change="(val) => changeSwitch(val)" v-model="isDark"/>
|
||||
<el-switch
|
||||
@change="switchDark"
|
||||
inline-prompt
|
||||
v-model="themeConfig.isDark"
|
||||
:active-icon="Sunny"
|
||||
:inactive-icon="Moon"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="switchDark">
|
||||
import { Sunny, Moon } from "@element-plus/icons-vue";
|
||||
import {computed, ref} from "vue";
|
||||
import { useDark, useToggle } from '@vueuse/core'
|
||||
import {useSettingStore} from "@/store/modules/setting"
|
||||
|
||||
const SettingStore = useSettingStore()
|
||||
// // 横向
|
||||
const isDark = ref(SettingStore.themeConfig.isDark)
|
||||
const changeSwitch = ()=>{
|
||||
let isDark = useDark()
|
||||
useToggle(isDark)
|
||||
}
|
||||
// 设置信息
|
||||
const themeConfig = computed(()=>SettingStore.themeConfig)
|
||||
|
||||
// 切换暗黑模式
|
||||
const switchDark = () => {
|
||||
const body = document.documentElement as HTMLElement;
|
||||
if (themeConfig.value.isDark) body.setAttribute("class", "dark");
|
||||
else body.setAttribute("class", "");
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,80 @@
|
|||
<template>
|
||||
<el-dropdown @command="commandAction">
|
||||
<span class="el-dropdown-link">
|
||||
<el-avatar :size="30" class="avatar" :src="AvatarLogo"/>
|
||||
{{userInfo.username}}
|
||||
<el-icon class="el-icon--right">
|
||||
<arrow-down />
|
||||
</el-icon>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item :command="2" >
|
||||
<el-icon><Edit /></el-icon>修改密码</el-dropdown-item>
|
||||
<el-dropdown-item :command="1" divided>
|
||||
<el-icon><SwitchButton /></el-icon>退出登录</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
|
||||
<PersonalDialog ref="person"/>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useRouter } from 'vue-router'
|
||||
import {ElMessage, ElMessageBox} from "element-plus";
|
||||
import {computed, ref} from "vue";
|
||||
|
||||
import AvatarLogo from '@/assets/image/avatar.png'
|
||||
import {useUserStore} from "@/store/modules/user"
|
||||
import {useTagsViewStore} from "@/store/modules/tagsView"
|
||||
import PersonalDialog from './PersonalDialog.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const UserStore = useUserStore()
|
||||
const TagsViewStore = useTagsViewStore()
|
||||
|
||||
// 用户信息
|
||||
const userInfo = computed(() => UserStore.userInfo)
|
||||
const person = ref()
|
||||
|
||||
const logOut = async () => {
|
||||
ElMessageBox.confirm('您是否确认退出登录?', '温馨提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(async () => {
|
||||
await router.push({path: '/login'})
|
||||
await UserStore.logout()
|
||||
TagsViewStore.clearVisitedView()
|
||||
ElMessage({
|
||||
type: "success",
|
||||
message: "退出登录成功!"
|
||||
});
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
const commandAction = (key: number) => {
|
||||
switch (key) {
|
||||
case 1:
|
||||
logOut()
|
||||
break
|
||||
case 2:
|
||||
person.value.show()
|
||||
break
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.avatar{
|
||||
margin-right: 6px
|
||||
}
|
||||
.el-dropdown-link {
|
||||
cursor: pointer;
|
||||
//color: var(--el-color-primary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<el-dialog v-model="dialogVisible" title="修改密码" width="60%">
|
||||
<el-dialog v-model="dialogVisible" title="修改密码" width="40%">
|
||||
<el-form
|
||||
ref="ruleFormRef"
|
||||
:model="ruleForm"
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
.mobile {
|
||||
.m-layout-header {
|
||||
left: 0 !important;
|
||||
width: 100%!important;
|
||||
}
|
||||
}
|
||||
.header {
|
||||
height: 50px;
|
||||
width: 100%;
|
||||
border-bottom: 1px solid #eee;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 10px 0 0;
|
||||
box-sizing: border-box;
|
||||
justify-content: space-between;
|
||||
.left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
}
|
||||
.tool-bar-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.right-item-menu{
|
||||
margin-right: 22px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.zb-fixed-header{
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 9;
|
||||
}
|
||||
.zb-no-fixed-header{
|
||||
width: 100%!important;;
|
||||
}
|
||||
|
||||
.m-layout-header {
|
||||
width: 100%;
|
||||
background: white;
|
||||
transition: width 0.28s;
|
||||
flex-shrink: 0;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 1px 4px rgb(0 21 41 / 8%);
|
||||
}
|
||||
.fixed-header-collapse{
|
||||
width: calc(100% - 60px);
|
||||
}
|
||||
.fixed-header-no-collapse{
|
||||
width: calc(100% - 210px);
|
||||
}
|
||||
.el-dropdown {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.transverseMenu{
|
||||
display: flex;
|
||||
.el-menu{
|
||||
overflow: hidden;
|
||||
}
|
||||
.tool-bar-right{
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
min-width:300px ;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -6,52 +6,27 @@
|
|||
mode === 'horizontal'?'':isCollapse?'fixed-header-collapse':'fixed-header-no-collapse'
|
||||
]"
|
||||
>
|
||||
<div
|
||||
class="header"
|
||||
:class="{
|
||||
transverseMenu: mode === 'horizontal',
|
||||
}"
|
||||
>
|
||||
<div class="header" :class="{ transverseMenu: mode === 'horizontal', }">
|
||||
<UMenu v-if="mode === 'horizontal'" />
|
||||
|
||||
<div class="left" v-if="mode === 'vertical'">
|
||||
<CollapseIcon/>
|
||||
<Hamburger />
|
||||
</div>
|
||||
|
||||
<div class="tool-bar-right">
|
||||
<div class="right">
|
||||
<language class="right-item-menu"/>
|
||||
|
||||
<GlobalComSize class="right-item-menu"/>
|
||||
|
||||
<HeaderSearch class="right-item-menu"/>
|
||||
|
||||
<Remind class="right-item-menu"/>
|
||||
|
||||
<ScreenFull class="right-item-menu"/>
|
||||
|
||||
<Setting class="right-item-menu"/>
|
||||
|
||||
<el-dropdown @command="commandAction">
|
||||
<span class="el-dropdown-link">
|
||||
<el-avatar :icon="UserFilled" :size="30" style="margin-right: 6px" />{{
|
||||
userInfo.username
|
||||
}}
|
||||
<el-icon class="el-icon--right">
|
||||
<arrow-down />
|
||||
</el-icon>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item :command="2" >
|
||||
<el-icon><Edit /></el-icon>修改密码</el-dropdown-item>
|
||||
<el-dropdown-item :command="1" divided>
|
||||
<el-icon><SwitchButton /></el-icon>退出登录</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
<Avatar/>
|
||||
</div>
|
||||
</div>
|
||||
<tag-views v-if="showTag" />
|
||||
<personal ref="person" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -67,141 +42,27 @@
|
|||
import Remind from './components/Remind'
|
||||
import HeaderSearch from './components/HeaderSearch'
|
||||
import CollapseIcon from './components/CollapseIcon'
|
||||
import Avatar from './components/Avatar'
|
||||
import UMenu from '../Sidebar/components/Menu.vue'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ElMessageBox, ElMessage } from 'element-plus'
|
||||
import {useSettingStore} from "@/store/modules/setting"
|
||||
import {useUserStore} from "@/store/modules/user"
|
||||
import {usePermissionStore} from "@/store/modules/permission"
|
||||
import {useTagsViewStore} from "@/store/modules/tagsView"
|
||||
|
||||
const person = ref()
|
||||
const router = useRouter()
|
||||
const SettingStore = useSettingStore()
|
||||
const UserStore = useUserStore()
|
||||
const PermissionStore = usePermissionStore()
|
||||
const TagsViewStore = useTagsViewStore()
|
||||
|
||||
const isCollapse = computed(() =>!SettingStore.isCollapse)
|
||||
// menu 布局
|
||||
const mode = computed(() => SettingStore.themeConfig.mode)
|
||||
// 显示 tag
|
||||
const showTag = computed(() =>SettingStore.themeConfig.showTag)
|
||||
// 用户信息
|
||||
const userInfo = computed(() => UserStore.userInfo)
|
||||
|
||||
const logOut = async () => {
|
||||
ElMessageBox.confirm('确定退出登录吗?', '退出登录', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(async () => {
|
||||
try {
|
||||
router.push({ path: '/login' })
|
||||
await UserStore.logout()
|
||||
PermissionStore.clearRoutes()
|
||||
TagsViewStore.clearVisitedView()
|
||||
ElMessage({
|
||||
type: "success",
|
||||
message: "退出登录成功!"
|
||||
});
|
||||
} catch (e) {}
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
const commandAction = (key: number) => {
|
||||
switch (key) {
|
||||
case 1:
|
||||
logOut()
|
||||
break
|
||||
case 2:
|
||||
person.value.show()
|
||||
break
|
||||
}
|
||||
}
|
||||
const handleCollapse = () => {
|
||||
SettingStore.setCollapse(isCollapse.value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.mobile {
|
||||
.m-layout-header {
|
||||
left: 0 !important;
|
||||
width: 100%!important;
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
height: 50px;
|
||||
width: 100%;
|
||||
border-bottom: 1px solid #eee;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 10px 0 0;
|
||||
box-sizing: border-box;
|
||||
justify-content: space-between;
|
||||
.left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
}
|
||||
.right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.right-item-menu{
|
||||
margin-right: 22px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.zb-fixed-header{
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 9;
|
||||
}
|
||||
.zb-no-fixed-header{
|
||||
width: 100%!important;;
|
||||
}
|
||||
|
||||
.m-layout-header {
|
||||
width: 100%;
|
||||
background: white;
|
||||
transition: width 0.28s;
|
||||
flex-shrink: 0;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 1px 4px rgb(0 21 41 / 8%);
|
||||
}
|
||||
.fixed-header-collapse{
|
||||
width: calc(100% - 60px);
|
||||
}
|
||||
.fixed-header-no-collapse{
|
||||
width: calc(100% - 210px);
|
||||
}
|
||||
.el-dropdown {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
}
|
||||
.el-dropdown-link {
|
||||
cursor: pointer;
|
||||
color: var(--el-color-primary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.transverseMenu{
|
||||
display: flex;
|
||||
.el-menu{
|
||||
overflow: hidden;
|
||||
}
|
||||
.right{
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
min-width:300px ;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
@import "./index";
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -21,9 +21,9 @@
|
|||
import {useSettingStore} from "@/store/modules/setting"
|
||||
import {usePermissionStore} from "@/store/modules/permission"
|
||||
const SettingStore = useSettingStore()
|
||||
const PermissionStor = usePermissionStore()
|
||||
const PermissionStore = usePermissionStore()
|
||||
|
||||
const cacheRoutes = computed(() =>PermissionStor.getCacheRoutes())
|
||||
const cacheRoutes = computed(() =>PermissionStore.getCacheRoutes())
|
||||
const isReload = computed(() => SettingStore.isReload)
|
||||
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { createApp } from 'vue'
|
|||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import pinia from "./store";
|
||||
import 'default-passive-events'
|
||||
// import 'default-passive-events'
|
||||
|
||||
// 权限路由
|
||||
import './permission'
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import chatRouter from './modules/chat'
|
|||
import othersRouter from './modules/other'
|
||||
import externalLink from './modules/externalLink'
|
||||
import formRouter from './modules/from'
|
||||
import iframeRouter from './modules/iframe'
|
||||
|
||||
// 异步组件
|
||||
export const asyncRoutes = [
|
||||
|
|
@ -30,6 +31,7 @@ export const asyncRoutes = [
|
|||
...errorRouter,
|
||||
...externalLink,
|
||||
...systemRouter,
|
||||
...iframeRouter,
|
||||
{
|
||||
path: '/:pathMatch(.*)',
|
||||
redirect: '/404'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
/** When your routing table is too long, you can split it into small modules**/
|
||||
|
||||
import Layout from "@/layout/index.vue";
|
||||
|
||||
const iframeRouter = [{
|
||||
path: '/iframe',
|
||||
component: Layout,
|
||||
redirect: '/iframe/embedded',
|
||||
name: 'iframe',
|
||||
meta: {
|
||||
title: '内嵌 iframe',
|
||||
icon: 'Memo'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'embedded',
|
||||
component: () => import('@/views/iframe/embedded/index.vue'),
|
||||
name: 'embedded',
|
||||
meta: { title: '内嵌 iframe', icon: 'Postcard' }
|
||||
},
|
||||
]
|
||||
}]
|
||||
|
||||
export default iframeRouter
|
||||
|
|
@ -94,7 +94,7 @@ const othersRouter = [{
|
|||
path: 'count',
|
||||
component: () => import('@/views/other/count/index.vue'),
|
||||
name: 'count',
|
||||
meta: { title: '数字自增长', icon: 'MenuIcon' }
|
||||
meta: { title: '数字动画', icon: 'MenuIcon' }
|
||||
}
|
||||
]
|
||||
}]
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<div class="contact-nickname">{{ item.created_at }} 林峰</div>
|
||||
<div class="receive-message-wrap">
|
||||
<div class="avatar-show">
|
||||
<img src="@/assets/image/avator.png" />
|
||||
<img src="@/assets/image/avatar.png" />
|
||||
</div>
|
||||
<div style="position: relative; display: flex">
|
||||
<div class="receive-message-info" v-html="item.content"></div>
|
||||
|
|
@ -30,7 +30,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="avatar-show">
|
||||
<img src="@/assets/image/avator.png" />
|
||||
<img src="@/assets/image/avatar.png" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -53,7 +53,7 @@
|
|||
<script lang="ts" setup>
|
||||
import { onMounted, ref, nextTick } from 'vue'
|
||||
import UToolbar from '../u-toolbar/index.vue'
|
||||
import defalutAvator from '@/assets/image/avator.png'
|
||||
import defalutAvator from '@/assets/image/avatar.png'
|
||||
import { chatData } from './chat.js'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import dayjs from 'dayjs'
|
||||
|
|
|
|||
|
|
@ -157,9 +157,9 @@
|
|||
<script setup lang="ts">
|
||||
import { User } from '@element-plus/icons-vue'
|
||||
import { ref } from 'vue'
|
||||
import AvatarLogo from '@/assets/image/avatar.png'
|
||||
import CountTo from '@/components/CountTo/index.vue'
|
||||
import BarCharts from '@/views/charts/components/simple/bar.vue'
|
||||
const circleUrl = ref('https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png')
|
||||
|
||||
const goTo = (url) => {
|
||||
window.open(url, '_blank')
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<div class="app-container-inner">
|
||||
<iframe src="https://ext.dcloud.net.cn/plugin?id=7511"
|
||||
frameborder="0" class="full-iframe"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts" name="embedded"></script>
|
||||
<style scoped lang="scss">
|
||||
.full-iframe{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
export const iconData = [
|
||||
'management',
|
||||
'baseball',
|
||||
'Basketball',
|
||||
'BellFilled',
|
||||
'Bell',
|
||||
'AddLocation',
|
||||
'Aim',
|
||||
'AlarmClock',
|
||||
'Apple',
|
||||
'ArrowDownBold',
|
||||
'Burger',
|
||||
'Brush',
|
||||
'BrushFilled',
|
||||
'RemoveFilled',
|
||||
'QuestionFilled',
|
||||
'Promotion',
|
||||
'Printer',
|
||||
'School',
|
||||
'Setting',
|
||||
'WarningFilled',
|
||||
'ZoomOut',
|
||||
'WalletFilled',
|
||||
'User',
|
||||
'ToiletPaper', 'Sunrise', 'Sunny', 'SwitchButton', 'TakeawayBox', 'Ticket',
|
||||
'StarFilled', 'Stamp', 'Stopwatch', 'SortDown', 'SemiSelect', 'Search'
|
||||
]
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
.m-card-drag{
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.header{
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
.footer{
|
||||
flex: 1;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
}
|
||||
.card {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 20px;
|
||||
overflow-x: hidden;
|
||||
background-color: var(--el-fill-color-blank);
|
||||
border: 1px solid var(--el-border-color-light);
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 0 12px rgb(0 0 0 / 5%);
|
||||
}
|
||||
.item-group-item {
|
||||
position: relative;
|
||||
width: calc(100% / 5.3);
|
||||
margin-bottom: 10px;
|
||||
flex-direction: column;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 100px;
|
||||
cursor: move;
|
||||
font-weight: 500;
|
||||
border: 1px solid rgba(0, 0, 0, 0.125);
|
||||
}
|
||||
.chosenClass {
|
||||
opacity: 0.6;
|
||||
border: 1px solid rgba(64,158,255,0.2);
|
||||
}
|
||||
.item-icon{
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center
|
||||
}
|
||||
|
|
@ -1,182 +1,67 @@
|
|||
<template>
|
||||
<u-container-layout class="row">
|
||||
<div style="margin-bottom: 10px">卡片拖拽排序</div>
|
||||
<div style="display: flex;align-items: center">
|
||||
<el-button class="btn" @click="sorter" type="primary" style="margin-bottom: 20px"
|
||||
>还原</el-button
|
||||
>
|
||||
<el-button
|
||||
class="btn"
|
||||
@click="getDataAction"
|
||||
type="primary"
|
||||
style="margin-bottom: 20px; margin-left: 20px"
|
||||
>获取数据</el-button
|
||||
>
|
||||
</div>
|
||||
<div class="col-6" style="">
|
||||
<el-row :gutter="10">
|
||||
<draggable
|
||||
:list="message"
|
||||
:animation="300"
|
||||
@end="drag = false"
|
||||
@start="drag = true"
|
||||
item-key="order"
|
||||
v-bind="dragOptions"
|
||||
class="list-group"
|
||||
ghost-class="ghost"
|
||||
<div class="m-card-drag">
|
||||
<el-card class="header">
|
||||
<el-button @click="sorter" type="primary" >还原</el-button>
|
||||
<el-button @click="getDataAction" type="primary" style="margin-left: 20px">获取数据</el-button>
|
||||
</el-card>
|
||||
|
||||
<div class="footer">
|
||||
<draggable
|
||||
class="card grid-container"
|
||||
v-model="dragList"
|
||||
item-key="id"
|
||||
animation="300"
|
||||
chosenClass="chosenClass"
|
||||
@change="onMoveCallback"
|
||||
:move="getdata"
|
||||
>
|
||||
<template #item="{ element }">
|
||||
<el-col :xs="6" :sm="6" :md="6" :lg="4" :xl="4">
|
||||
<el-card shadow="hover" class="list-group-item">
|
||||
<el-icon
|
||||
:size="30"
|
||||
:color="element.color"
|
||||
style="width: 100%; display: flex; justify-content: center"
|
||||
>
|
||||
<component :is="element.icon"></component>
|
||||
</el-icon>
|
||||
<div style="margin-top: 10px">按住拖拽排序</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
</draggable>
|
||||
</el-row>
|
||||
forceFallback="true"
|
||||
>
|
||||
<template #item="{ element }">
|
||||
<div class="item-group-item" >
|
||||
<el-icon
|
||||
:size="30"
|
||||
:color="element.color"
|
||||
class="item-icon"
|
||||
>
|
||||
<component :is="element.icon"></component>
|
||||
</el-icon>
|
||||
<div style="margin-top: 10px">按住拖拽排序</div>
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
</div>
|
||||
</u-container-layout>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
// 参考文档 https://sortablejs.github.io/vue.draggable.next/#/transition-example-2
|
||||
// https://github.com/SortableJS/vue.draggable.next/blob/master/package.json
|
||||
import draggable from 'vuedraggable'
|
||||
import { computed, ref, reactive } from 'vue'
|
||||
import {ElMessage, ElMessageBox} from "element-plus";
|
||||
import { ref} from 'vue'
|
||||
import { getColor } from '@/utils/index'
|
||||
import {iconData} from './data'
|
||||
|
||||
const message = ref([])
|
||||
const tags = ref([])
|
||||
|
||||
const icon = [
|
||||
'management',
|
||||
'baseball',
|
||||
'Basketball',
|
||||
'BellFilled',
|
||||
'Bell',
|
||||
'AddLocation',
|
||||
'Aim',
|
||||
'AlarmClock',
|
||||
'Apple',
|
||||
'ArrowDownBold',
|
||||
'Burger',
|
||||
'Brush',
|
||||
'BrushFilled',
|
||||
'RemoveFilled',
|
||||
'QuestionFilled',
|
||||
'Promotion',
|
||||
'Printer',
|
||||
'School',
|
||||
'Setting',
|
||||
'WarningFilled',
|
||||
'ZoomOut',
|
||||
'WalletFilled',
|
||||
'User',
|
||||
// 'ToiletPaper', 'Sunrise', 'Sunny', 'SwitchButton', 'TakeawayBox', 'Ticket',
|
||||
// 'StarFilled', 'Stamp', 'Stopwatch', 'SortDown', 'SemiSelect', 'Search'
|
||||
]
|
||||
|
||||
const dragList = ref([])
|
||||
const drag = ref(false)
|
||||
// 随机生成颜色
|
||||
for (let i = 0; i < 30; i++) {
|
||||
message.value.push({
|
||||
name: i,
|
||||
order: i + 1,
|
||||
icon: icon[i],
|
||||
for (let i = 0; i < 20; i++) {
|
||||
dragList.value.push({
|
||||
id: i + 1,
|
||||
icon: iconData[i],
|
||||
color: getColor(),
|
||||
})
|
||||
}
|
||||
|
||||
const drag = ref(false)
|
||||
|
||||
// 设置排序参数
|
||||
const dragOptions = computed(() => {
|
||||
return {
|
||||
animation: 200,
|
||||
group: 'description',
|
||||
disabled: false,
|
||||
ghostClass: 'ghost', // 滑动颜色
|
||||
}
|
||||
})
|
||||
|
||||
const sorter = () => {
|
||||
message.value = message.value.sort((a, b) => a.name - b.name)
|
||||
dragList.value = dragList.value.sort((a, b) => a.id - b.id)
|
||||
ElMessage.success(`还原成功`)
|
||||
}
|
||||
|
||||
const onMoveCallback = (val) => {
|
||||
console.log('拖动前的索引 :' + val.moved.newIndex)
|
||||
console.log('拖动后的索引 :' + val.moved.oldIndex)
|
||||
}
|
||||
// 查看最新的数据
|
||||
const getdata = () => {
|
||||
console.log(11111111)
|
||||
}
|
||||
// 查看最新的数据
|
||||
const getDataAction = () => {
|
||||
console.log(message.value)
|
||||
ElMessage.success(`获取数据:${JSON.stringify(dragList.value)}`)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.list-group-item {
|
||||
position: relative;
|
||||
/*width: 100px;*/
|
||||
min-width: 160px;
|
||||
margin-right: 10px;
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 100px;
|
||||
|
||||
/*padding: 0.75rem 1.25rem;*/
|
||||
background-color: #fff;
|
||||
font-weight: 500;
|
||||
/*font-size: 24px;*/
|
||||
border: 1px solid rgba(0, 0, 0, 0.125);
|
||||
}
|
||||
.button {
|
||||
margin-top: 35px;
|
||||
}
|
||||
.flip-list-move {
|
||||
transition: transform 0.5s;
|
||||
}
|
||||
.no-move {
|
||||
transition: transform 0s;
|
||||
}
|
||||
.ghost {
|
||||
opacity: 0.5;
|
||||
background: #c8ebfb;
|
||||
}
|
||||
.list-group {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
/*.list-group {*/
|
||||
/* display: flex;*/
|
||||
/* display: -webkit-box;*/
|
||||
/* display: flex;*/
|
||||
/* -ms-flex-direction: column;*/
|
||||
/* -webkit-box-orient: vertical;*/
|
||||
/* -webkit-box-direction: normal;*/
|
||||
/* //flex-direction: column;*/
|
||||
/* padding-left: 0;*/
|
||||
/* flex-wrap: wrap;*/
|
||||
/* margin-bottom: 0;*/
|
||||
/* border-radius: 0.25rem;*/
|
||||
|
||||
/*}*/
|
||||
.list-group-item {
|
||||
cursor: move;
|
||||
}
|
||||
.list-group-item i {
|
||||
cursor: pointer;
|
||||
}
|
||||
@import "./index";
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,101 @@
|
|||
<template>
|
||||
<div class="m-custom-cropper">
|
||||
<el-card class="box-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>自定义裁剪</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="content">
|
||||
<div class="side-a">
|
||||
<div class="avatar-cropper">
|
||||
<AvatarCropper
|
||||
:options="{
|
||||
autoCropWidth:200,
|
||||
autoCropHeight:200
|
||||
}"
|
||||
:avatarUrl="avatarUrl"
|
||||
@change="realTime" ref="avatar"/>
|
||||
</div>
|
||||
<div>
|
||||
<el-button type="primary" @click.prevent="rotateLeft">向左旋转</el-button>
|
||||
<el-button type="primary" @click.prevent="rotateRight">向右旋转</el-button>
|
||||
<el-button type="primary" @click.prevent="zoom(1)">放大</el-button>
|
||||
<el-button type="primary" @click.prevent="zoom(-1)">缩小</el-button>
|
||||
<el-button type="primary" @click.prevent="down('base64')">下载图片</el-button>
|
||||
<el-button type="primary" @click.prevent="down('base64')">下载图片</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="side-b">
|
||||
<div class="preview-wrap" :style="previewStyle">
|
||||
<img :src="previewsOption.url" class="uploadImg" :style="previewsOption.img"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {reactive, ref} from 'vue'
|
||||
import AvatarCropper from '@/components/AvatarCropper/index.vue'
|
||||
import avatarUrlLogo from '@/assets/image/cro-avatar.jpg'
|
||||
import {ElMessage} from "element-plus";
|
||||
|
||||
let defaultUrl = ref<string>(avatarUrlLogo)
|
||||
let avatarUrl = ref<string>(avatarUrlLogo)
|
||||
let previewStyle = ref({})
|
||||
let previewsOption = ref({})
|
||||
let avatar = ref()
|
||||
|
||||
const realTime = (data)=>{
|
||||
previewsOption.value = data
|
||||
let options = {
|
||||
width: data.w + "px",
|
||||
height: data.h + "px",
|
||||
overflow: "hidden",
|
||||
|
||||
}
|
||||
previewStyle.value = {
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
const rotateLeft = () => {
|
||||
avatar.value.rotateLeft()
|
||||
}
|
||||
const rotateRight = () => {
|
||||
avatar.value.rotateRight()
|
||||
}
|
||||
|
||||
const zoom = (percent) => {
|
||||
avatar.value.zoom(percent)
|
||||
}
|
||||
|
||||
const down = async (type) => {
|
||||
let aLink = document.createElement('a')
|
||||
aLink.download = '下载裁剪图片'
|
||||
let data = await avatar.value.getBase64()
|
||||
aLink.href = data
|
||||
aLink.click()
|
||||
}
|
||||
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.m-custom-cropper{
|
||||
margin-top: 20px;
|
||||
.content{
|
||||
display: flex;
|
||||
}
|
||||
.side-a{
|
||||
margin-right: 100px;
|
||||
.avatar-cropper{
|
||||
width: 500px;
|
||||
height: 400px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
.side-b{
|
||||
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,164 @@
|
|||
<template>
|
||||
<div class="m-header-avatar">
|
||||
<el-card class="box-card">
|
||||
<template #header>
|
||||
<span>头像上传裁剪</span>
|
||||
</template>
|
||||
<div class="avatar" @click="avatarUpload">
|
||||
<img :src="defaultUrl" class="avatar"/>
|
||||
<el-icon class="icon-upload"><Plus /></el-icon>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
title="头像上传"
|
||||
top="30px"
|
||||
width="700px"
|
||||
>
|
||||
<div class="upload-dialog">
|
||||
<div class="slide-a">
|
||||
<div class="avatar-cropper" >
|
||||
<AvatarCropper :avatarUrl="avatarUrl" @change="realTime" ref="avatar"/>
|
||||
</div>
|
||||
|
||||
<div style="display: flex;justify-content: center;margin-top: 10px">
|
||||
<el-upload action="/" multiple :before-upload="beforeUploadAction" accept="image/*">
|
||||
<el-button type="primary">点击上传图片<el-icon><Upload /></el-icon></el-button>
|
||||
</el-upload>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="slide-b">
|
||||
<div class="preview-wrap" :style="previewStyle">
|
||||
<img :src="previewsOption.url" class="uploadImg" :style="previewsOption.img"/>
|
||||
</div>
|
||||
|
||||
<div class="preview-wrap" :style="previewStyle1">
|
||||
<img :src="previewsOption.url" class="uploadImg" :style="previewsOption.img"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="configUpload">
|
||||
确认
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {reactive, ref} from 'vue'
|
||||
import AvatarCropper from '@/components/AvatarCropper/index.vue'
|
||||
import avatarUrlLogo from '@/assets/image/cro-avatar.jpg'
|
||||
import {ElMessage} from "element-plus";
|
||||
|
||||
let defaultUrl = ref<string>(avatarUrlLogo)
|
||||
let avatarUrl = ref<string>(avatarUrlLogo)
|
||||
let dialogVisible = ref<boolean>(false)
|
||||
let avatar = ref()
|
||||
let previewsOption = ref({})
|
||||
let previewStyle = ref({})
|
||||
let previewStyle1 = ref({})
|
||||
const avatarUpload = ()=>{
|
||||
dialogVisible.value = true
|
||||
}
|
||||
const beforeUploadAction = (file, fileLi) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
var reader = new FileReader()
|
||||
let reg = /\.jpg$|\.jpeg$|\.gif$|\.png$/i
|
||||
reader.readAsDataURL(file)
|
||||
let name = file.name
|
||||
if (reg.test(name)) {
|
||||
reader.onload = (e: FileReader) => {
|
||||
avatarUrl.value = e.target.result
|
||||
resolve(e.target.result)
|
||||
}
|
||||
} else {
|
||||
ElMessage.error('请上传图片')
|
||||
reject()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const configUpload = async ()=>{
|
||||
defaultUrl.value = await avatar.value.getBase64()
|
||||
dialogVisible.value = false
|
||||
ElMessage.success('裁剪成功')
|
||||
}
|
||||
|
||||
|
||||
|
||||
const realTime = (data)=>{
|
||||
previewsOption.value = data
|
||||
let options = {
|
||||
width: data.w + "px",
|
||||
height: data.h + "px",
|
||||
overflow: "hidden",
|
||||
|
||||
}
|
||||
previewStyle.value = {
|
||||
...options,
|
||||
zoom: (200 / data.w)
|
||||
|
||||
}
|
||||
previewStyle1.value = {
|
||||
...options,
|
||||
zoom: (120 / data.w)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.m-header-avatar{
|
||||
width: 100%;
|
||||
.avatar{
|
||||
border-radius: 50%;
|
||||
width: 160px;
|
||||
height: 160px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
img{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
.icon-upload{
|
||||
color: white;
|
||||
font-size: 60px;
|
||||
position: absolute;
|
||||
display: none;
|
||||
}
|
||||
.avatar:hover{
|
||||
.icon-upload{
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
.avatar-cropper{
|
||||
width: 400px;
|
||||
height: 400px;
|
||||
}
|
||||
.upload-dialog{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.preview-wrap{
|
||||
width: 160px;
|
||||
height: 160px;
|
||||
overflow: hidden;
|
||||
border-radius: 50%;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
.slide-b{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,207 +1,14 @@
|
|||
<template>
|
||||
<u-container-layout class="m-cropper">
|
||||
<div style="display: flex">
|
||||
<div class="left">
|
||||
<div class="cropper-content">
|
||||
<vue-cropper
|
||||
ref="cropper"
|
||||
:img="option.img"
|
||||
:output-size="option.size"
|
||||
:output-type="option.outputType"
|
||||
:info="true"
|
||||
:full="option.full"
|
||||
:fixed="option.fixed"
|
||||
:fixed-number="option.fixedNumber"
|
||||
:can-move="option.canMove"
|
||||
:can-move-box="option.canMoveBox"
|
||||
:autoCropWidth="option.autoCropWidth"
|
||||
:autoCropHeight="option.autoCropHeight"
|
||||
:fixed-box="option.fixedBox"
|
||||
:auto-crop="option.autoCrop"
|
||||
:auto-crop-height="option.autoCropHeight"
|
||||
:center-box="option.centerBox"
|
||||
@real-time="realTime"
|
||||
/>
|
||||
</div>
|
||||
<el-button type="primary" @click.prevent="zoom(1)">放大</el-button>
|
||||
<el-button type="primary" @click.prevent="zoom(-1)">缩小</el-button>
|
||||
<el-button type="primary" @click.prevent="rotateLeft">向左旋转</el-button>
|
||||
<el-button type="primary" @click.prevent="rotateRight">向右旋转</el-button>
|
||||
<el-button type="primary" @click.prevent="reset">清除</el-button>
|
||||
<el-button type="primary" @click.prevent="cropImage">获取结果</el-button>
|
||||
<el-button type="primary" @click.prevent="down('base64')">下载图片</el-button>
|
||||
<el-upload class="upload-demo" :show-file-list="false" action :before-upload="beforeUpload">
|
||||
<el-button type="primary">选择上传图片</el-button>
|
||||
</el-upload>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4 style="margin-top: 10px">实时预览</h4>
|
||||
<div
|
||||
class="show-preview"
|
||||
:style="{
|
||||
width: option.previews.w + 'px',
|
||||
height: option.previews.h + 'px',
|
||||
overflow: 'hidden',
|
||||
margin: '5px',
|
||||
}"
|
||||
>
|
||||
<div :style="option.previews.div">
|
||||
<img :src="option.previews.url" :style="option.previews.img" />
|
||||
</div>
|
||||
</div>
|
||||
<h4 style="margin-top: 10px">获取结果</h4>
|
||||
<div style="background: #ccc; width: 100px; height: 100px">
|
||||
<img :src="option.cropImg" style="width: 100%; height: 100%" v-if="option.cropImg" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</u-container-layout>
|
||||
<div class="app-container">
|
||||
<HeaderAvatar/>
|
||||
<CustomCropper/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
// http://github.xyxiao.cn/vue-cropper/example/
|
||||
// https://codepen.io/xyxiao001/pen/yLooYKg
|
||||
import 'vue-cropper/dist/index.css'
|
||||
import { VueCropper } from 'vue-cropper'
|
||||
import imgSrc from '@/assets/image/cro.jpg'
|
||||
import { reactive, ref } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import type { UploadProps, UploadUserFile } from 'element-plus'
|
||||
const cropper = ref()
|
||||
const option = reactive({
|
||||
img: imgSrc,
|
||||
size: 1,
|
||||
full: false, // 是否输出原图比例的截图
|
||||
outputType: 'png',
|
||||
canMove: true,
|
||||
fixed: true, // 是否开启截图框宽高固定比例
|
||||
info: true, // 裁剪框的大小信息
|
||||
outputSize: 0.8, // 裁剪生成图片的质量
|
||||
fixedBox: false, // 固定截图框大小 不允许改变
|
||||
original: false, // 上传图片按照原始比例渲染
|
||||
canMoveBox: true, // 截图框能否拖动
|
||||
autoCrop: true, // 是否默认生成截图框
|
||||
autoCropWidth: 300, // 默认生成截图框宽度
|
||||
autoCropHeight: 300, // 默认生成截图框高度
|
||||
// 只有自动截图开启 宽度高度才生效
|
||||
centerBox: false, // 截图框是否被限制在图片里面
|
||||
high: true,
|
||||
max: 99999,
|
||||
previews: {
|
||||
url: '',
|
||||
},
|
||||
cropImg: '',
|
||||
})
|
||||
|
||||
const realTime = (data) => {
|
||||
option.previews = data
|
||||
}
|
||||
|
||||
const cropmove = (e) => {
|
||||
// console.log('eeeeee',e)
|
||||
}
|
||||
|
||||
const zoom = (percent) => {
|
||||
cropper.value.changeScale(percent)
|
||||
}
|
||||
const rotateLeft = () => {
|
||||
cropper.value.rotateLeft()
|
||||
}
|
||||
const rotateRight = () => {
|
||||
cropper.value.rotateRight()
|
||||
}
|
||||
|
||||
const move = (offsetX, offsetY) => {
|
||||
cropper.value.move(offsetX, offsetY)
|
||||
}
|
||||
|
||||
const down = (type) => {
|
||||
// event.preventDefault()
|
||||
var aLink = document.createElement('a')
|
||||
aLink.download = 'demo'
|
||||
// 输出
|
||||
if (type === 'blob') {
|
||||
this.$refs.cropper.getCropBlob((data) => {
|
||||
// this.downImg = window.URL.createObjectURL(data);
|
||||
aLink.href = window.URL.createObjectURL(data)
|
||||
aLink.click()
|
||||
})
|
||||
} else {
|
||||
cropper.value.getCropData((data) => {
|
||||
// this.downImg = data;
|
||||
aLink.href = data
|
||||
aLink.click()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const rotate = (deg) => {
|
||||
cropper.value.rotate(deg)
|
||||
}
|
||||
|
||||
const cropImage = () => {
|
||||
cropper.value.getCropData((data) => {
|
||||
option.cropImg = data
|
||||
})
|
||||
}
|
||||
|
||||
const reset = () => {
|
||||
option.img = ''
|
||||
option.cropImg = ''
|
||||
}
|
||||
|
||||
const beforeUpload = (file, fileList) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
var reader = new FileReader()
|
||||
// let res = !/\.(gif|jpg|jpeg|png|bmp|GIF|JPG|PNG)$/
|
||||
let reg = /\.jpg$|\.jpeg$|\.gif$|\.png$/i
|
||||
reader.readAsDataURL(file)
|
||||
let name = file.name
|
||||
if (reg.test(name)) {
|
||||
reader.onload = (e: FileReader) => {
|
||||
let data
|
||||
if (typeof e.target.result === 'object') {
|
||||
// 把Array Buffer转化为blob 如果是base64不需要
|
||||
data = window.URL.createObjectURL(new Blob([e.target.result]))
|
||||
} else {
|
||||
data = e.target.result
|
||||
}
|
||||
resolve(e.target.result)
|
||||
option.imgSrc = data
|
||||
option.img = data
|
||||
}
|
||||
} else {
|
||||
ElMessage.error('请上传图片')
|
||||
reject()
|
||||
}
|
||||
})
|
||||
}
|
||||
import HeaderAvatar from './components/HeaderAvatar'
|
||||
import CustomCropper from './components/CustomCropper'
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.cropper-content {
|
||||
display: flex;
|
||||
width: 400px;
|
||||
height: 400px;
|
||||
text-align: center;
|
||||
}
|
||||
::v-deep(.el-button) {
|
||||
margin-top: 15px;
|
||||
}
|
||||
.m-cropper {
|
||||
width: 100%;
|
||||
/*height: 500px;*/
|
||||
display: flex;
|
||||
.left {
|
||||
width: 400px;
|
||||
margin-right: 30px;
|
||||
}
|
||||
.preview {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
margin-top: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Reference in New Issue