Skip to content

Commit

Permalink
add img cutter
Browse files Browse the repository at this point in the history
  • Loading branch information
Sjj1024 committed Oct 21, 2024
1 parent dfd696d commit 3f40b0f
Show file tree
Hide file tree
Showing 7 changed files with 236 additions and 22 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"sass": "^1.78.0",
"sass-loader": "^16.0.1",
"vue": "^3.3.4",
"vue-cropper": "^1.1.4",
"vue-i18n": "^10.0.0",
"vue-router": "4"
},
Expand Down
7 changes: 7 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion src/App.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<script setup lang="ts">
import { onMounted } from 'vue'
import { appDataDir } from '@tauri-apps/api/path'
import { createDir, BaseDirectory } from '@tauri-apps/api/fs'
const userLanguage = navigator.language
Expand Down
132 changes: 132 additions & 0 deletions src/components/CutterImg.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
<template>
<div class="avatar-container">
<el-dialog
:title="title"
:model-value="visible"
width="400px"
append-to-body
@opened="openDialog"
:before-close="beforeClose"
>
<div class="imgBox">
<vue-cropper
ref="cropper"
:img="imgUrl"
:info="true"
:autoCrop="true"
:autoCropWidth="200"
:autoCropHeight="200"
:fixedBox="false"
outputType="png"
:fixedNumber="[1, 1]"
:fixed="true"
:centerBox="true"
v-if="showCropper"
/>
</div>
<div class="footerBtn">
<el-button :icon="Plus" @click="changeScale(1)"></el-button>
<el-button :icon="Minus" @click="changeScale(-1)"></el-button>
<el-button
:icon="RefreshLeft"
@click="rotateLeft()"
></el-button>
<el-button
:icon="RefreshRight"
@click="rotateRight()"
></el-button>
<el-button @click="determine"> 确定 </el-button>
</div>
</el-dialog>
</div>
</template>

<script setup lang="ts">
import { Plus, Minus, RefreshLeft, RefreshRight } from '@element-plus/icons-vue'
import 'vue-cropper/dist/index.css'
import { VueCropper } from 'vue-cropper'
import { getCurrentInstance, ref, reactive } from 'vue'
const { proxy }: any = getCurrentInstance()
const props = defineProps({
imgUrl: {
type: String,
default: '',
},
title: {
type: String,
default: '编辑图片',
},
confirm: {
type: Function,
default: () => {},
},
})
const visible = defineModel({ type: Boolean, default: false })
const showCropper = ref(false)
const openDialog = () => {
showCropper.value = true
}
const changeScale = (num: any) => {
num = num || 1
proxy.$refs.cropper.changeScale(num)
}
const rotateLeft = () => {
proxy.$refs.cropper.rotateLeft()
}
const rotateRight = () => {
proxy.$refs.cropper.rotateRight()
}
const beforeClose = () => {
visible.value = false
}
const determine = () => {
visible.value = false
proxy.$refs.cropper.getCropData((data: any) => {
// do something
console.log('data', data)
props.confirm(data)
})
}
</script>

<style lang="scss" scoped>
.avatar-container {
.img-box {
border-radius: 50%;
border: 1px solid #ccc;
width: 10vw;
height: 10vw;
}
}
.imgBox {
height: 300px;
}
.footerBtn {
margin-top: 10px;
display: flex;
flex-direction: row;
justify-content: space-evenly;
align-items: center;
}
.preview-box {
position: absolute;
top: 50%;
transform: translate(50%, -50%);
width: 200px;
height: 200px;
border-radius: 50%;
border: 1px solid #ccc;
overflow: hidden;
}
</style>
1 change: 1 addition & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import '@/assets/fonts/iconfont.css'
import 'element-plus/dist/index.css'
import '@/assets/theme.css'
import '@/assets/global.scss'
import 'vue-cropper/dist/index.css'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'

const app = createApp(App)
Expand Down
45 changes: 36 additions & 9 deletions src/pages/edit.vue
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,12 @@
</div>
</template>
</el-dialog>
<!-- cutter img -->
<CutterImg
v-model="cutVisible"
:imgUrl="iconBase64"
:confirm="confirmIcon"
></CutterImg>
</div>
</template>

Expand All @@ -213,12 +219,15 @@ import { appDataDir, join } from '@tauri-apps/api/path'
import { convertFileSrc } from '@tauri-apps/api/tauri'
import { open } from '@tauri-apps/api/dialog'
import { ArrowLeft } from '@element-plus/icons-vue'
import CutterImg from '@/components/CutterImg.vue'
import { useI18n } from 'vue-i18n'
import { openUrl } from '@/utils/common'
const router = useRouter()
const store = usePakeStore()
const { t } = useI18n()
const iconBase64 = ref('')
const cutVisible = ref(false)
const centerDialogVisible = ref(false)
const formSize = ref<ComponentSize>('default')
const appFormRef = ref<FormInstance>()
Expand All @@ -233,6 +242,7 @@ const appForm = reactive({
height: store.currentProject.height || 600,
desc: store.currentProject.desc,
})
const iconFileName = ref('')
const appRules = reactive<FormRules>({
url: [
Expand Down Expand Up @@ -286,17 +296,28 @@ const appRules = reactive<FormRules>({
],
})
// 上传icon
// icon confirm
const confirmIcon = (base64Data: string) => {
cutVisible.value = false
console.log('confirmIcon base64Data', base64Data)
const base64Img = base64Data.split('base64,')[1]
// update icon file content
updateIcon(base64Img)
// save image to datadir
saveImage(iconFileName.value, base64Img)
}
// upload icon
const uploadIcon = async () => {
console.log('uploadIcon')
// document.getElementById('open')!.click()
// use tauri open api, bacause input cant seleted file type
const selectedFilePath: any = await open({
multiple: false, // 只允许选择一个文件
multiple: false,
filters: [
{
name: 'Images',
extensions: ['png'],
extensions: ['png', 'jpg', 'jpeg'],
},
],
})
Expand All @@ -306,16 +327,15 @@ const uploadIcon = async () => {
return null
}
const fileName = selectedFilePath.split('/').pop()
iconFileName.value = fileName
console.log('Selected file path:', selectedFilePath, fileName)
appForm.icon = 'assets%2F' + fileName
// get file name
const binaryData = await readBinaryFile(selectedFilePath)
const base64Data = arrayBufferToBase64(binaryData)
console.log('Base64 encoded image:', base64Data)
// update icon file content
updateIcon(base64Data) // 更新icon文件内容
// save image to datadir
saveImage(fileName, base64Data)
iconBase64.value = 'data:image/jpg;base64,' + base64Data
cutVisible.value = true
}
// update icon file content
Expand Down Expand Up @@ -679,7 +699,7 @@ const checkBuildStatus = async () => {
if (checkRes.status === 200 && checkRes.data.total_count > 0) {
const build_runs = checkRes.data.workflow_runs[0]
// check build status
const { status, conclusion } = build_runs
const { status, conclusion, html_url } = build_runs
buildStatus = statusMap[status] || '构建中'
// 除了第一次,别的都不需要
// document.querySelector('.el-loading-text')!.innerHTML = '构建成功'
Expand All @@ -699,6 +719,13 @@ const checkBuildStatus = async () => {
// 清理构建定时器
buildSecondTimer && clearInterval(buildSecondTimer)
checkDispatchTimer && clearInterval(checkDispatchTimer)
} else if (status === 'failure') {
buildLoading.value = false
buildTime = 0
openUrl(html_url)
document.querySelector('.el-loading-text')!.innerHTML = '构建失败'
buildSecondTimer && clearInterval(buildSecondTimer)
checkDispatchTimer && clearInterval(checkDispatchTimer)
}
} else {
buildTime = 0
Expand Down
Loading

0 comments on commit 3f40b0f

Please sign in to comment.