Compare commits

...

2 Commits

Author SHA1 Message Date
aixianling
c6224e52ba Merge remote-tracking branch 'origin/dev' into devops
# Conflicts:
#	library/apps/AppBuilding/AiImage.vue
#	library/apps/AppBuilding/AiUploader.vue
2024-11-01 09:45:38 +08:00
liuye
a1a923772d 事件添加 2024-11-01 09:30:03 +08:00
3 changed files with 365 additions and 0 deletions

View File

@@ -0,0 +1,80 @@
<template>
<section class="AiImage">
<div v-if="$slots.default" @tap="prev">
<slot/>
</div>
<u-image v-else :src="image" @tap="prev">
<image v-if="link" class="errorImage" slot="error" :src="$cdn+'link.png'"/>
<image v-else-if="miniapp" class="errorImage" slot="error" :src="$cdn+'miniwxmp.jpg'"/>
<div v-else-if="$slots.errorImage" slot="error">
<slot name="errorImage"/>
</div>
<image v-else class="errorImage" slot="error" :src="$cdn+'file.png'"/>
</u-image>
</section>
</template>
<script>
import {mapActions} from "vuex";
export default {
name: "AiImage",
data() {
return {
dialog: false
}
},
computed: {
image() {
return this.src?.replace(/\\/g, '/')
}
},
props: {
src: String,
preview: Boolean,
link: Boolean,
miniapp: Boolean,
file: {
default: () => {
}
}
},
methods: {
...mapActions(['previewFile', 'injectJWeixin']),
prev() {
if (this.preview) {
if (!!this.image) {
uni.previewImage({
current: this.image,
urls: [this.image],
success() {
sessionStorage.setItem("previewImage", " 1")
}
})
} else {
this.previewFile({size: 1, ...this.file})
}
}
},
}
}
</script>
<style lang="scss" scoped>
.AiImage {
::v-deep image {
width: 160px;
height: 160px;
object-fit: cover;
}
::v-deep .u-image__error {
position: relative;
}
.errorImage {
width: 80px;
height: 80px;
}
}
</style>

View File

@@ -0,0 +1,283 @@
<template>
<div class="ai-uploader">
<div class="fileList">
<div class="item" v-for="(item, i) in fileList" :key="i">
<template v-if="type == 'image'">
<ai-image :src="item.url" :preview="preview"/>
<u-icon v-if="!disabled" class="delBtn" color="#f46" name="close-circle-fill" size="40" @click="remove(i)"/>
</template>
<template v-else-if="type == 'video'">
<video :src="item.url" :poster="item.thumb"/>
<u-icon v-if="!disabled" class="delBtn" color="#f46" name="close-circle-fill" size="40" @click="remove(i)"/>
</template>
<div class="file" v-else>
<ai-image :preview="preview" :file="item"/>
<div class="info">
<span>{{ item.name }} </span>
<i>{{ item.fileSizeStr }}</i>
</div>
<template v-if="!disabled">
<div btn @tap="handleReUpload(i)">
重新上传
</div>
<div btn @tap="remove(i)">
删除
</div>
</template>
</div>
</div>
<div v-if="!disabled&&(fileList.length == 0 || (multiple && fileList.length < limit))" class="default"
@click="upload">
<i class="iconfont iconfont-iconAdd"/>
<span>{{ placeholder }}</span>
</div>
</div>
</div>
</template>
<script>
import {mapState} from 'vuex'
import AiImage from './AiImage'
export default {
name: 'AiUploader',
components: {AiImage},
props: {
limit: {default: 1}, //数量
placeholder: {default: '添加图片'}, // 文字提示
type: {default: 'image'}, // 文件类型image还是file
multiple: {
type: Boolean,
default: false,
},
fileId: String,
mediaId: String,
def: {default: () => []},
action: String,
preview: Boolean,
size: {default: 10 * 1024 * 1024},
disabled: Boolean,
sourceType: {default: () => ['album', 'camera']},
withoutToken: {
type: Number,
default: 0,
},
},
computed: {
...mapState(['token']),
errorImage: v => v.$cdn + 'file.png',
api() {
if (this.action) return this.action
else return {
image: '/admin/file/add',
file: '/admin/file/add',
video: '/admin/file/addVideo',
}[this.type]
}
},
watch: {
def: {
handler(v) {
if (!!v?.toString()) {
if (this.multiple) {
this.fileList = v
} else if (v?.url) {
this.fileList = [v]
}
}
},
immediate: true,
deep: true
},
},
data() {
return {
fileList: [],
}
},
methods: {
remove(index) {
this.fileList.splice(index, 1)
this.$emit('list', this.fileList)
},
upload(wait) {
typeof wait == 'function' && wait()
let count = this.limit - (this.fileList?.length || 0)
if (count > 0) {
let params = {
count,
sizeType: ['compressed'],
sourceType: [this.sourceType].flat(),
success: (res) => {
let count = this.fileList?.length + (res.tempFiles?.length || res.tempFile ? 1 : 0)
if (count > this.limit && this.limit !== 1) {
return this.$u.toast(`不能超过${this.limit}`)
}
if (res.tempFiles?.length > 0) {
res.tempFiles.map(this.uploadFile)
} else if (res?.tempFile) {
this.uploadFile(res.tempFile)
}
},
}
if (this.type == 'image') {
uni.chooseImage(params)
} else if (this.type == 'video') {
uni.chooseVideo(params)
} else {
uni.chooseFile(params)
}
} else {
this.$u.toast(`不能超过${this.limit}`)
}
},
uploadFile(img) {
if (this.size > 0 && img.size > this.size) {
return this.$u.toast(`不能超过${Math.ceil(this.size / 1024 / 1024)}MB`)
}
uni.showLoading({title: '上传中'})
let formData = new FormData()
formData.append('file', img)
if (this.manual) {
this.$emit('manual', img)
uni.hideLoading()
} else {
this.$http.post(this.api, formData, {
params: {type: this.type},
withoutToken: this.withoutToken == 1 ? true : false,
}).then((res) => {
if (res?.data) {
this.$emit('data', res.data)
this.$u.toast('上传成功!')
if (!this.action) {
if (this.type == 'image') {
const [image = "",] = res.data
this.fileList.push({url: image.split(";")[0], id: image.split(";")?.[1]})
} else if (this.type == 'video') {
const [video = "", thumb = ""] = res.data
this.fileList.push({url: video.split(";")[0], id: video.split(";")?.[1], thumb: thumb.split(";")[0]})
}
} else if (this.api == '/app/wxcp/upload/uploadFile') {
this.$emit('update:mediaId', res.data?.media?.mediaId)
this.$emit('update:fileId', res.data.file.id)
this.fileList.push(res.data.file)
} else if (this.api == '/admin/file/add2') {
let info = res.data
this.$emit('update:fileId', info?.id)
this.fileList.push(res.data)
} else if (this.api == '/admin/file/add-portrait') {
this.fileList.push({url: res.data?.split(";")?.[0], id: res.data?.split(";")?.[1]})
}
this.$emit("update:def", this.fileList)
this.$emit("list", this.fileList)
} else {
this.$u.toast(res.msg)
}
}).catch(err => {
this.$u.toast(err)
}).finally(() => uni.hideLoading())
}
},
handleReUpload(i) {
this.upload(() => this.remove(i))
},
},
}
</script>
<style lang="scss" scoped>
.ai-uploader {
width: 100%;
line-height: normal;
margin-bottom: 16px;
::v-deep.fileList {
display: flex;
flex-wrap: wrap;
.item {
width: initial;
padding: 0 10px 10px 0;
position: relative;
.delBtn {
position: absolute;
right: -8px;
top: -8px;
z-index: 2;
border-radius: 50%;
overflow: hidden;
background-color: #fff;
height: 40px;
}
image, video {
width: 29vw;
height: 29vw;
}
i {
font-style: normal;
color: #9b9b9b;
}
.info {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
align-items: flex-start;
overflow: hidden;
text-overflow: ellipsis;
& > span {
flex: 1;
min-width: 0;
}
}
div[btn] {
color: $uni-color-primary;
}
div:nth-child(4) {
color: #f72c27;
}
& > * + * {
margin-left: 20px;
}
}
.default {
width: 30vw;
height: 30vw;
box-sizing: border-box;
border-radius: 8px;
background: #f3f4f7;
color: #89b;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.iconfont-iconAdd {
font-size: 64px;
}
span {
display: block;
text-align: center;
font-size: 28px;
}
}
.file {
width: 100vw;
display: flex;
align-items: center;
}
}
}
</style>

View File

@@ -50,9 +50,11 @@
<script>
import {mapState} from 'vuex'
import AiUploader from './AiUploader'
export default {
name: 'AppPatrolReport',
appName: '事件添加',
components: {AiUploader},
data() {
return {
forms: {