<template> | |||||
<div class="oss-filed"> | |||||
<n-upload | |||||
action="#" | |||||
:max="limit" | |||||
:file-list="data.fileList" | |||||
:default-upload="false" | |||||
:show-remove-button="remBtn" | |||||
:list-type="fileType == 'img' ? 'image-card' : 'text'" | |||||
@change="handleChange" | |||||
@before-upload="beforeUpload" | |||||
> | |||||
<n-button v-if="fileType != 'img'"> | |||||
<n-icon> | |||||
<FileOutlined /> | |||||
</n-icon> | |||||
{{ btnName }} | |||||
</n-button> | |||||
<div v-if="fileType == 'img'" class="oss-upload-text" /> | |||||
</n-upload> | |||||
</div> | |||||
</template> | |||||
<script setup name="UploadOss"> | |||||
import OSS from 'ali-oss' | |||||
import { getOssAuth } from '@/api/common/upload.js' | |||||
import { ref, reactive, watch, onMounted, toRefs } from 'vue' | |||||
import { FileOutlined } from '@vicons/antd' | |||||
const props = defineProps({ | |||||
limit: { | |||||
type: Number, | |||||
default: 1 | |||||
}, | |||||
defaultList: { | |||||
type: String, | |||||
default: '' | |||||
}, | |||||
fileType: { | |||||
type: String, | |||||
default: 'img' | |||||
}, | |||||
btnName: { | |||||
type: String, | |||||
default: '选择文件' | |||||
}, | |||||
remBtn: { | |||||
type: Boolean, | |||||
default: false | |||||
}, | |||||
keyName: { | |||||
type: String, | |||||
default: 'src' | |||||
} | |||||
}) | |||||
const emit = defineEmits(['uploadStatus']) | |||||
const client = ref(null) // 上传实例 | |||||
const data = reactive({ | |||||
...props, | |||||
client: null, | |||||
fileList: [], | |||||
hasUploadInfo: {} | |||||
}) | |||||
onMounted(() => { | |||||
if (props.defaultList) { | |||||
const list = props.defaultList.split(',') | |||||
data.fileList = list.map((item) => { | |||||
const objectName = item.split('/').slice(3).join('/') | |||||
const obj = { | |||||
name: objectName, | |||||
id: objectName, | |||||
// type: 'image/png', | |||||
url: item, | |||||
objectName: objectName, | |||||
status: 'finished' | |||||
} | |||||
data.hasUploadInfo[obj.id] = { | |||||
id: objectName, | |||||
objectName: objectName | |||||
} | |||||
return obj | |||||
}) | |||||
emit('uploadStatus', 'upload') | |||||
} else { | |||||
data.fileList = [] | |||||
} | |||||
}) | |||||
/* 监听默认文件列表 */ | |||||
watch(() => props.defaultList, (value) => { | |||||
if (value.length) { | |||||
const list = value.split(',') | |||||
const fileList = list.map((item) => { | |||||
const objectName = item.split('/').slice(3).join('/') | |||||
const obj = { | |||||
name: objectName, | |||||
id: objectName, | |||||
type: 'image/png', | |||||
url: item, | |||||
objectName: objectName | |||||
} | |||||
data.hasUploadInfo[obj.id] = { | |||||
id: objectName, | |||||
objectName: objectName | |||||
} | |||||
return obj | |||||
}) | |||||
data.fileList = fileList | |||||
emit('uploadStatus', 'upload') | |||||
} else { | |||||
data.fileList = [] | |||||
// emit('uploadStatus', 'hasFile') | |||||
} | |||||
}) | |||||
/** | |||||
* @description: 上传前的判断文件类型 | |||||
* @param {*} file 当前选择的文件 | |||||
* @return {*} | |||||
*/ | |||||
const beforeUpload = ({ file }) => { | |||||
if (props.fileType === 'img') { | |||||
if (!file.type.match('image.*')) { | |||||
$message.error('请选择正确的图片类型') | |||||
/* 中断流程 */ | |||||
return false | |||||
} | |||||
} | |||||
if (props.fileType === 'srt') { | |||||
const type = ['srt', 'SRT'] | |||||
const temp = type.includes(file.name.split('.')[1]) | |||||
if (!temp) { | |||||
$message.error('请选择SRT类型文件') | |||||
/* 中断流程 */ | |||||
return false | |||||
} | |||||
} | |||||
if (props.fileType === 'tif') { | |||||
const type = ['tif', 'TIF'] | |||||
const temp = type.includes(file.name.split('.')[1]) | |||||
if (!temp) { | |||||
$message.error('请选择tif类型文件') | |||||
/* 中断流程 */ | |||||
return false | |||||
} | |||||
} | |||||
} | |||||
/** | |||||
* @description: 文件发生变化时 | |||||
* @param {Array} fileList | |||||
* @return {*} | |||||
*/ | |||||
const handleChange = async({ fileList }) => { | |||||
data.fileList = fileList.map((item) => { | |||||
if (Object.keys(data.hasUploadInfo).includes(item.id)) { | |||||
item.objectName = data.hasUploadInfo[item.id].objectName | |||||
} | |||||
return item | |||||
}) | |||||
/* 判断是否存在文件 */ | |||||
const status = fileList.length ? 'ready' : 'no-file' | |||||
emit('uploadStatus', status) | |||||
// await startUpload() | |||||
} | |||||
/** | |||||
* @description: 上传文件 | |||||
* @return {*} | |||||
*/ | |||||
const startUpload = () => { | |||||
return new Promise((resolve, reject) => { | |||||
/* 定义要上传文件 */ | |||||
const uploads = [] | |||||
/* 从待文件列表中获取没有上传过的文件 */ | |||||
const uloadList = data.fileList.filter((item) => { | |||||
return !item.url | |||||
}) | |||||
/* 如果待上传列表长度为0,则返回,否则获取上传鉴权 */ | |||||
if (!uloadList.length) { | |||||
/* 如果没有要上传的文件信息则返回oss的文件名 */ | |||||
/* 不存在objectName的文件表示上传失败 */ | |||||
const objectList = data.fileList.map((item) => item.objectName || 'error') | |||||
resolve(objectList) | |||||
} else { | |||||
getOssAuth().then(res => { | |||||
/* 初始化oss上传客户端 */ | |||||
data.client = new OSS({ | |||||
region: 'oss-cn-shanghai', | |||||
secure: true, | |||||
accessKeyId: res.data.accessKeyId, | |||||
accessKeySecret: res.data.accessKeySecret, | |||||
stsToken: res.data.securityToken, | |||||
bucket: 'ta-tech-image' | |||||
}) | |||||
/* promise上传要上传的文件 */ | |||||
uloadList.forEach(item => { | |||||
uploads.push(uploadOss(item)) | |||||
}) | |||||
Promise.all(uploads).then((response) => { | |||||
/* 获取fileList内的文件存储名称 */ | |||||
const objectList = data.fileList.map((item) => item.objectName).filter(it => it) | |||||
// resolve(objectList) | |||||
resolve({ [props.keyName]: objectList.join() }) | |||||
emit('uploadStatus', 'success') | |||||
}).catch((error) => { | |||||
console.log('error', error) | |||||
resolve(error) | |||||
}) | |||||
}) | |||||
} | |||||
}) | |||||
} | |||||
/** | |||||
* 上传到 OSS | |||||
* @param {*} files | |||||
*/ | |||||
const uploadOss = (files) => { | |||||
const file = files.file | |||||
return new Promise((resolve, reject) => { | |||||
/* 生成随机文件名称 */ | |||||
const randomString = Math.random().toString(36).slice(2) | |||||
const suffix = /\.[^\.]+/.exec(file.name) | |||||
const timestamp = new Date().getTime() | |||||
const objectName = `/imagedir/${randomString}_${timestamp}${suffix}` | |||||
/* 上传文件至oss */ | |||||
data.client.put(objectName, file) | |||||
.then((res) => { | |||||
const index = data.fileList.findIndex(list => list.id === files.id) | |||||
data.fileList[index].url = res.url | |||||
data.fileList[index].objectName = objectName | |||||
const id = data.fileList[index].id | |||||
data.hasUploadInfo[id] = { | |||||
id: objectName, | |||||
objectName: objectName | |||||
} | |||||
resolve(objectName) | |||||
}) | |||||
.catch((err) => { | |||||
resolve('error') | |||||
console.log(err) | |||||
}) | |||||
}) | |||||
} | |||||
defineExpose({ startUpload, ...toRefs(data) }) | |||||
</script> | |||||
<style lang="scss" scoped> | |||||
.oss-filed { | |||||
width: 100%; | |||||
padding: 10px 20px; | |||||
} | |||||
.oss-upload-picture-card-wrapper { | |||||
min-height: 122px; | |||||
padding: 10px 0 0 10px; | |||||
} | |||||
.oss-upload-text { | |||||
// width: 100px; | |||||
// height: 100px; | |||||
// cursor: pointer; | |||||
// border: 1px solid #e1e1e1; | |||||
// margin: 10px 0 0 10px; | |||||
position: relative; | |||||
} | |||||
.oss-upload-text::before { | |||||
content: ''; | |||||
position: absolute; | |||||
left: 50%; | |||||
top: 50%; | |||||
width: 30px; | |||||
transform: translate(-50%, -50%); | |||||
border-top: 2px solid #e1e1e1; | |||||
} | |||||
.oss-upload-text::after { | |||||
content: ''; | |||||
position: absolute; | |||||
left: 50%; | |||||
top: 50%; | |||||
height: 30px; | |||||
transform: translate(-50%, -50%); | |||||
border-left: 2px solid #e1e1e1; | |||||
} | |||||
.n-image { | |||||
img { | |||||
overflow: hidden; | |||||
} | |||||
} | |||||
.oss-upload-type { | |||||
text-align: center; | |||||
font-size: 14px; | |||||
color: rgb(64, 152, 252); | |||||
} | |||||
</style> |
:file-list="fileList" | :file-list="fileList" | ||||
:default-upload="false" | :default-upload="false" | ||||
list-type="image-card" | list-type="image-card" | ||||
:show-remove-button="remBtn" | |||||
@change="handleChange" | @change="handleChange" | ||||
@before-upload="beforeUpload" | @before-upload="beforeUpload" | ||||
> | > | ||||
defaultList: { | defaultList: { | ||||
type: String, | type: String, | ||||
default: '' | default: '' | ||||
}, | |||||
remBtn: { | |||||
type: Boolean, | |||||
default: false | |||||
} | } | ||||
}, | }, | ||||
emits: ['uploadStatus'], | emits: ['uploadStatus'], |
<n-icon> | <n-icon> | ||||
<FileOutlined /> | <FileOutlined /> | ||||
</n-icon> | </n-icon> | ||||
选择文件 | |||||
{{ uploadName }} | |||||
</n-button> | </n-button> | ||||
</n-upload> | </n-upload> | ||||
<n-button | |||||
<!-- <n-button | |||||
v-if="!autoUpload" | v-if="!autoUpload" | ||||
type="primary" | type="primary" | ||||
:disabled="data.fileList.length === 0" | :disabled="data.fileList.length === 0" | ||||
style="margin-top: 16px" | style="margin-top: 16px" | ||||
class="btn" | class="btn" | ||||
@click="handleUploadStart" | @click="handleUploadStart" | ||||
> | > | ||||
<n-icon> | <n-icon> | ||||
<UploadOutlined /> | <UploadOutlined /> | ||||
</n-icon> | </n-icon> | ||||
{{ text }} | |||||
</n-button> | |||||
开始上传 | |||||
</n-button> --> | |||||
</template> | </template> | ||||
type: Boolean, | type: Boolean, | ||||
default: false | default: false | ||||
}, | }, | ||||
text: { | |||||
uploadName: { | |||||
type: String, | type: String, | ||||
default: '开始上传' | |||||
default: '选择文件' | |||||
} | } | ||||
}) | }) | ||||
const emit = defineEmits(['uploadStatus']) | const emit = defineEmits(['uploadStatus']) | ||||
* @return {*} | * @return {*} | ||||
*/ | */ | ||||
const beforeUpload = ({ file }) => { | const beforeUpload = ({ file }) => { | ||||
// if (!file.type.match('video.*')) { | |||||
// $message.error('请选择正确的视频') | |||||
// return false | |||||
// } else { | |||||
const hasSelect = data.fileList.some((item) => item.name === file.name) | |||||
if (!hasSelect) { | |||||
data.uploadFile.push(file.file) | |||||
} else { | |||||
$message.error('您已选择过该文件') | |||||
if (!file.type.match('video.*')) { | |||||
$message.error('请选择正确的视频') | |||||
return false | return false | ||||
} else { | |||||
const hasSelect = data.fileList.some((item) => item.name === file.name) | |||||
if (!hasSelect) { | |||||
data.uploadFile.push(file.file) | |||||
} else { | |||||
$message.error('您已选择过该文件') | |||||
return false | |||||
} | |||||
} | } | ||||
// } | |||||
} | } | ||||
/** | /** | ||||
}, | }, | ||||
// 开始上传 | // 开始上传 | ||||
onUploadstarted: function(uploadInfo) { | onUploadstarted: function(uploadInfo) { | ||||
console.log('uploader', uploader) | |||||
if (uploadInfo.videoId) { | if (uploadInfo.videoId) { | ||||
// 如果uploadInfo.videoId存在,调用刷新视频上传凭证接口 | // 如果uploadInfo.videoId存在,调用刷新视频上传凭证接口 | ||||
refreshAuth(uploadInfo.videoId).then(res => { | refreshAuth(uploadInfo.videoId).then(res => { | ||||
uploader.setUploadAuthAndAddress(uploadInfo, res.data.uploadAuth, res.data.uploadAddress, res.data.videoId) | uploader.setUploadAuthAndAddress(uploadInfo, res.data.uploadAuth, res.data.uploadAddress, res.data.videoId) | ||||
data.videoId = res.data.videoId | |||||
}) | }) | ||||
} else { | } else { | ||||
const params = { | const params = { | ||||
} | } | ||||
getAuth(params).then(res => { | getAuth(params).then(res => { | ||||
uploader.setUploadAuthAndAddress(uploadInfo, res.data.uploadAuth, res.data.uploadAddress, res.data.videoId) | uploader.setUploadAuthAndAddress(uploadInfo, res.data.uploadAuth, res.data.uploadAddress, res.data.videoId) | ||||
data.videoId = res.data.videoId | |||||
}) | }) | ||||
} | } | ||||
}, | }, | ||||
onUploadSucceed: function(uploadInfo) { | onUploadSucceed: function(uploadInfo) { | ||||
const fileName = uploadInfo.file.name | const fileName = uploadInfo.file.name | ||||
data.fileList.forEach((item) => { | data.fileList.forEach((item) => { | ||||
console.log(item) | |||||
if (item.name === fileName) { | if (item.name === fileName) { | ||||
data.readyFile[item.uid].url = uploadInfo.object | |||||
data.readyFile[item.uid].url = '/' + uploadInfo.object | |||||
} | } | ||||
}) | }) | ||||
}, | }, | ||||
// 文件上传失败 | // 文件上传失败 | ||||
onUploadFailed: function(uploadInfo, code, message) { | onUploadFailed: function(uploadInfo, code, message) { | ||||
console.log('onUploadFailed: file:' + uploadInfo.file.name + ',code:' + code + ', message:' + message) | |||||
// console.log('onUploadFailed: file:' + uploadInfo.file.name + ',code:' + code + ', message:' + message) | |||||
}, | }, | ||||
// 取消文件上传 | // 取消文件上传 | ||||
// 全部文件上传结束 | // 全部文件上传结束 | ||||
onUploadEnd: function(uploadInfo) { | onUploadEnd: function(uploadInfo) { | ||||
// console.log('onUploadEnd: uploaded all the files', uploadInfo) | // console.log('onUploadEnd: uploaded all the files', uploadInfo) | ||||
emit('uploadStatus', { status: 'success', list: data.readyFile }) | |||||
emit('uploadStatus', { status: 'success', list: data.readyFile, videoIds: data.videoId }) | |||||
} | } | ||||
}) | }) | ||||
return uploader | return uploader | ||||
if (props.autoUpload) { // 判断是否自动上传 | if (props.autoUpload) { // 判断是否自动上传 | ||||
handleUploadStart() | handleUploadStart() | ||||
} | } | ||||
// handleUploadStart() | |||||
} | } | ||||
/** | /** | ||||
emit('uploadStatus', { status: 'uploading', list: data.readyFile }) | emit('uploadStatus', { status: 'uploading', list: data.readyFile }) | ||||
} | } | ||||
} | } | ||||
defineExpose({ data }) | |||||
</script> | </script> | ||||
<style lang="scss" scoped> | <style lang="scss" scoped> | ||||
} | } | ||||
.btn { | .btn { | ||||
width: 120px; | |||||
margin-left: calc(50% - 60px); | |||||
margin-top: 10px; | |||||
margin-bottom: 30px; | |||||
} | |||||
width: 120px; | |||||
margin-left: calc(50% - 60px); | |||||
margin-top: 10px; | |||||
margin-bottom: 30px; | |||||
} | |||||
</style> | </style> |
export const PHOTOGRAPHY_WAY = [ | export const PHOTOGRAPHY_WAY = [ | ||||
{ | { | ||||
label: '普通巡检', | label: '普通巡检', | ||||
value: '1' | |||||
value: 1 | |||||
}, | }, | ||||
{ | { | ||||
label: '正射影像', | label: '正射影像', | ||||
value: '2' | |||||
value: 2 | |||||
}, | }, | ||||
{ | { | ||||
label: '倾斜摄影', | label: '倾斜摄影', | ||||
value: '3' | |||||
value: 3 | |||||
} | } | ||||
] | ] |
emphasis: { | emphasis: { | ||||
focus: 'series' | focus: 'series' | ||||
}, | }, | ||||
name: '', | |||||
name: '任务数', | |||||
smooth: true, // 平滑曲线 | smooth: true, // 平滑曲线 | ||||
showSymbol: true, // 节点 | showSymbol: true, // 节点 | ||||
symbolSize: 6, | symbolSize: 6, | ||||
width: 3 | width: 3 | ||||
}, | }, | ||||
itemStyle: { | itemStyle: { | ||||
color: 'rgba(32,204,255, 0.8)' | |||||
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [ | |||||
{ offset: 0, color: 'rgba(32,204,255, 0.8)' }, | |||||
{ offset: 0.5, color: 'rgba(32,204,255, 0.8)' }, | |||||
{ offset: 0.7, color: 'rgba(32,204,255, 0.8)' }, | |||||
{ offset: 1, color: '#483D8B' } | |||||
]) | |||||
}, | }, | ||||
areaStyle: { | areaStyle: { | ||||
normal: { | normal: { | ||||
offset: 0, | offset: 0, | ||||
color: 'rgba(32,204,255, 0.8)' | color: 'rgba(32,204,255, 0.8)' | ||||
}, | }, | ||||
{ | |||||
offset: 0.5, | |||||
color: 'rgba(32,204,255, 0.4)' | |||||
}, | |||||
{ | { | ||||
offset: 1, | offset: 1, | ||||
color: 'rgba(60,212,191, 0.1)' | |||||
color: 'rgba(60,212,191, 0.05)' | |||||
} | } | ||||
]) | ]) | ||||
} | } | ||||
} | } | ||||
}], | }], | ||||
series: [{ | series: [{ | ||||
barWidth: 22, | |||||
barWidth: 12, | |||||
type: 'bar', | type: 'bar', | ||||
emphasis: { | emphasis: { | ||||
focus: 'series' | focus: 'series' |
require-mark-placement="left" | require-mark-placement="left" | ||||
> | > | ||||
<n-form-item label="横幅图片" path="cover"> | <n-form-item label="横幅图片" path="cover"> | ||||
<UploadOss ref="ossRefs" :default-list="initUpload" @upload-status="handleUploadStatus" /> | |||||
<UploadOss | |||||
ref="ossRefs" | |||||
:rem-btn="type !== 'preview'" | |||||
:default-list="initUpload" | |||||
@upload-status="handleUploadStatus" | |||||
/> | |||||
</n-form-item> | </n-form-item> | ||||
<!-- <n-form-item label="排序" path="sort"> | <!-- <n-form-item label="排序" path="sort"> | ||||
<n-input-number v-model:value="form.sort" clearable /> | <n-input-number v-model:value="form.sort" clearable /> | ||||
</template> | </template> | ||||
<script> | <script> | ||||
import { defineComponent, ref, reactive, computed, toRefs, watch } from 'vue' | |||||
import { defineComponent, ref, reactive, computed, toRefs, watch, getCurrentInstance } from 'vue' | |||||
import Modal from '@/components/Modal/index.vue' | import Modal from '@/components/Modal/index.vue' | ||||
import UploadOss from '@/components/UploadOss/index.vue' | |||||
// import UploadOss from '@/components/UploadOss/index.vue' | |||||
import UploadOss from '@/components/UploadOss/fileOss.vue' | |||||
import { bannerCreate, bannerUpdate } from '@/api/system/banner.js' | import { bannerCreate, bannerUpdate } from '@/api/system/banner.js' | ||||
export default defineComponent({ | export default defineComponent({ | ||||
name: 'BannerModal', | name: 'BannerModal', | ||||
} else { | } else { | ||||
data.initUpload = '' | data.initUpload = '' | ||||
} | } | ||||
}) | |||||
}, { immediate: true }) | |||||
const getModalOptions = computed(() => { | const getModalOptions = computed(() => { | ||||
return { | return { | ||||
const formRef = ref() | const formRef = ref() | ||||
const ossRefs = ref() | const ossRefs = ref() | ||||
function handleConfirm() { | function handleConfirm() { | ||||
if (props.type !== 'preview') { | |||||
formRef.value?.validate((errors) => { | |||||
if (!errors) { | |||||
const uploads = ossRefs.value.map((item, index) => { | |||||
return item.startUpload() | |||||
}) | |||||
Promise.all(uploads) | |||||
.then(response => { | |||||
const isError = response.map((item) => { | |||||
return item.includes('error') | |||||
}) | |||||
if (!isError.includes(true)) { | |||||
const imageStr = response.join() | |||||
const params = { | |||||
...data.form, | |||||
cover: imageStr[0] | |||||
} | |||||
if (params.id) { | |||||
/* 编辑 */ | |||||
bannerUpdate(params) | |||||
.then(res => { | |||||
if (res.code === 0) { | |||||
emit('reload') | |||||
handleClose() | |||||
} | |||||
}) | |||||
} else { | |||||
/* 新增 */ | |||||
bannerCreate(params) | |||||
.then(res => { | |||||
if (res.code === 0) { | |||||
emit('reload') | |||||
handleClose() | |||||
} | |||||
}) | |||||
} | |||||
} else { | |||||
$message.error('图片上传失败,请稍后重试') | |||||
formRef.value.validate().then(() => { | |||||
ossRefs.value.startUpload().then(res => { | |||||
const params = { | |||||
...data.form, | |||||
cover: res[0] | |||||
} | |||||
if (params.id) { | |||||
/* 编辑 */ | |||||
bannerUpdate(params) | |||||
.then(({ code }) => { | |||||
if (code === 0) { | |||||
emit('reload') | |||||
handleClose() | |||||
} | } | ||||
}) | }) | ||||
handleClose() | |||||
} else { | } else { | ||||
$message.error('请完善必填信息') | |||||
/* 新增 */ | |||||
bannerCreate(params) | |||||
.then(({ code }) => { | |||||
if (code === 0) { | |||||
emit('reload') | |||||
handleClose() | |||||
} | |||||
}) | |||||
} | } | ||||
}) | }) | ||||
} else { | |||||
handleClose() | |||||
} | |||||
}).catch(e => { | |||||
console.log('e:', e) | |||||
}) | |||||
} | } | ||||
/* 关闭弹窗 */ | /* 关闭弹窗 */ | ||||
getModalOptions, | getModalOptions, | ||||
handleUploadStatus, | handleUploadStatus, | ||||
handleConfirm, | handleConfirm, | ||||
handleClose | |||||
handleClose, | |||||
formRef, | |||||
ossRefs | |||||
} | } | ||||
} | } | ||||
}) | }) |
{ | { | ||||
title: '序号', | title: '序号', | ||||
key: 'key', | key: 'key', | ||||
width: 180, | |||||
render: (_, index) => { | render: (_, index) => { | ||||
return `${index + 1}` | return `${index + 1}` | ||||
}, | }, | ||||
render(row) { | render(row) { | ||||
return h(TableImage, { | return h(TableImage, { | ||||
images: { | images: { | ||||
width: 36, | |||||
height: 36, | |||||
width: 119, | |||||
height: 51, | |||||
src: row.cover | src: row.cover | ||||
// previewDisabled: true // 禁止点击预览 | |||||
} | } | ||||
}) | }) | ||||
} | } | ||||
title: '上传人', | title: '上传人', | ||||
key: 'updateUser', | key: 'updateUser', | ||||
align: 'center', | align: 'center', | ||||
width: 160 | |||||
width: 200 | |||||
}, | }, | ||||
{ | { | ||||
title: '上传时间', | title: '上传时间', | ||||
key: 'updateTime', | |||||
key: 'createTime', | |||||
align: 'center', | align: 'center', | ||||
width: 160 | |||||
width: 220 | |||||
}, | }, | ||||
{ | { | ||||
title: '操作', | title: '操作', | ||||
align: 'center', | align: 'center', | ||||
width: 150, | |||||
width: 220, | |||||
fixed: 'right', | fixed: 'right', | ||||
render(row) { | render(row) { | ||||
return h(TableAction, { | return h(TableAction, { |
<div v-if="current !== 1" class="cont"> | <div v-if="current !== 1" class="cont"> | ||||
<n-descriptions label-placement="left" label-align="right" :column="4" title="飞行信息"> | <n-descriptions label-placement="left" label-align="right" :column="4" title="飞行信息"> | ||||
<template v-for="(it, i) in flyInfo" :key="i + it.label"> | <template v-for="(it, i) in flyInfo" :key="i + it.label"> | ||||
<n-descriptions-item v-if="current !== it?.current" :label="it.label"> | |||||
<n-descriptions-item v-if="!it?.current || current >= it?.current" :label="it.label"> | |||||
{{ it.value }} | {{ it.value }} | ||||
</n-descriptions-item> | </n-descriptions-item> | ||||
</template> | </template> | ||||
<!-- 设备/影响基本信息 - 飞手接单: 管理员 --> | <!-- 设备/影响基本信息 - 飞手接单: 管理员 --> | ||||
<div v-if="current == 2 && isAdmin" class="cont"> | <div v-if="current == 2 && isAdmin" class="cont"> | ||||
<n-descriptions label-placement="left" label-align="right" :column="4" title="设备/影响基本信息"> | |||||
<n-descriptions label-placement="left" label-align="right" :column="4" title="设备/影像基本信息"> | |||||
<template v-for="(it, i) in equipmentInfo" :key="i + it.label"> | <template v-for="(it, i) in equipmentInfo" :key="i + it.label"> | ||||
<n-descriptions-item v-if="it?.isLive !== data.isLive" :label="it.label"> | <n-descriptions-item v-if="it?.isLive !== data.isLive" :label="it.label"> | ||||
{{ it.value }} | {{ it.value }} | ||||
<!-- 设备/影响基本信息 - 已接单 --> | <!-- 设备/影响基本信息 - 已接单 --> | ||||
<div v-if="current === 3" class="cont"> | <div v-if="current === 3" class="cont"> | ||||
<n-descriptions label-placement="left" label-align="right" :column="4" title="设备/影响基本信息"> | |||||
<n-descriptions label-placement="left" label-align="right" :column="4" title="设备/影像基本信息"> | |||||
<template v-for="(it, i) in executionInfo" :key="i + it.label"> | <template v-for="(it, i) in executionInfo" :key="i + it.label"> | ||||
<n-descriptions-item :label="it.label"> | <n-descriptions-item :label="it.label"> | ||||
{{ it.value }} | {{ it.value }} | ||||
<!-- 设备/影响基本信息 - 执行中: 管理员 --> | <!-- 设备/影响基本信息 - 执行中: 管理员 --> | ||||
<div v-if="current === 4" class="cont"> | <div v-if="current === 4" class="cont"> | ||||
<n-descriptions label-placement="left" label-align="right" :column="4" title="设备/影响基本信息"> | |||||
<n-descriptions label-placement="left" label-align="right" :column="4" title="设备/影像基本信息"> | |||||
<template v-for="(it, i) in executionInfo" :key="i + it.label"> | <template v-for="(it, i) in executionInfo" :key="i + it.label"> | ||||
<n-descriptions-item :label="it.label"> | <n-descriptions-item :label="it.label"> | ||||
{{ it.value }} | {{ it.value }} | ||||
<div v-if="current === 5" class="cont"> | <div v-if="current === 5" class="cont"> | ||||
<div class="title">飞行文件</div> | <div class="title">飞行文件</div> | ||||
<UploadVod @upload-status="vodStatus" /> | |||||
<UploadVod v-if="data.photographyWay === 1" ref="videoRefs" :auto-upload="true" :upload-name="'选择【视频】'" :limit="1" @upload-status="vodStatusVideo" /> | |||||
<fileOss v-if="data.photographyWay === 1" ref="srtRefs" :file-type="'srt'" :key-name="'srtUrl'" :btn-name="'选择【轨迹】'" :limit="1" :default-list="data.srtUrl" @upload-status="vodStatusSRT" /> | |||||
<fileOss v-if="data.photographyWay === 2" ref="ortRefs" :file-type="'tif'" :key-name="'orthoUrl'" :btn-name="'选择【正射影像】'" :limit="9" :default-list="data.orthoUrl" @upload-status="vodStatusZS" /> | |||||
<fileOss v-if="data.photographyWay === 3" ref="inclinedRefs" :file-type="'tif'" :key-name="'inclinedUrl'" :btn-name="'选择【倾斜影像】'" :limit="9" :default-list="data.inclinedUrl" @upload-status="vodStatusQX" /> | |||||
<n-button v-if="isAdmin" type="primary" class="btn" :loading="loading" @click="submitFile"> | |||||
<n-icon> | |||||
<UploadOutlined /> | |||||
</n-icon> | |||||
开始上传 | |||||
</n-button> | |||||
</div> | </div> | ||||
</template> | </template> | ||||
<script setup name="DrawComp"> | <script setup name="DrawComp"> | ||||
import { ref, defineProps, reactive, defineEmits } from 'vue' | |||||
import { ref, defineProps, reactive, defineEmits, watchEffect } from 'vue' | |||||
import { TASK_STATUS } from '@/utils/dictionary' | import { TASK_STATUS } from '@/utils/dictionary' | ||||
import { distributionPilot, pilotOrder, pilotStart, pilotEnd, uploadFlightUrl } from '@/api/task/index.js' | import { distributionPilot, pilotOrder, pilotStart, pilotEnd, uploadFlightUrl } from '@/api/task/index.js' | ||||
import { form, getPilotList, getEquipment, getEquipmentMount, getCloudMount } from '../tools/drawForm' | import { form, getPilotList, getEquipment, getEquipmentMount, getCloudMount } from '../tools/drawForm' | ||||
import { basic, fly, equipment, execution } from '../hook/index' | import { basic, fly, equipment, execution } from '../hook/index' | ||||
import UploadVod from '@/components/UploadVod/index.vue' | import UploadVod from '@/components/UploadVod/index.vue' | ||||
import fileOss from '@/components/UploadOss/fileOss.vue' | |||||
import { UploadOutlined } from '@vicons/antd' | |||||
getPilotList() // 获取飞手列表 | getPilotList() // 获取飞手列表 | ||||
getEquipment() // 无人机列表 | getEquipment() // 无人机列表 | ||||
}) | }) | ||||
const { detail: { data }} = props // 传来的详情数据 | const { detail: { data }} = props // 传来的详情数据 | ||||
form.userForm = data | |||||
const current = data.pilotStatus / 5 // 当前状态 | const current = data.pilotStatus / 5 // 当前状态 | ||||
const equipmentInfo = equipment(data) // 设备/影响基本信息 | const equipmentInfo = equipment(data) // 设备/影响基本信息 | ||||
const executionInfo = execution(data) // 执行中信息 | const executionInfo = execution(data) // 执行中信息 | ||||
const videoUrl = ref('') // 视频文件 | |||||
const videoId = ref('') // 视频文件 | |||||
const srtUrl = ref('') // srt文件 | |||||
// const inclinedUrl = ref('') // 倾斜摄影 | |||||
// const orthoUrl = ref('') // 正射影像 | |||||
const videoRefs = ref(null) // video refs | |||||
const srtRefs = ref(null) // srt refs | |||||
const ortRefs = ref(null) // 正射 refs | |||||
const inclinedRefs = ref(null) // 倾斜 refs | |||||
const loading = ref(false) | |||||
const isBtn = ref(false) | |||||
const emit = defineEmits(['close']) | const emit = defineEmits(['close']) | ||||
// 分配飞手 | // 分配飞手 | ||||
}) | }) | ||||
} | } | ||||
// 视频上传 | |||||
const vodStatus = ({ status, list }) => { | |||||
console.log(status, list) | |||||
const videoUrl = [] | |||||
// 上传 - 视频 | |||||
const vodStatusVideo = ({ status, list, videoIds }) => { | |||||
console.log(status, list, videoIds) | |||||
const arr = [] | |||||
for (var key in list) { | for (var key in list) { | ||||
videoUrl.push(list[key].url) | |||||
arr.push(list[key].url) | |||||
} | } | ||||
const videoList = videoUrl.join(',') | |||||
console.log('videoList', videoList) | |||||
return | |||||
if (status === 'success') { | if (status === 'success') { | ||||
// 视频上传 | |||||
uploadFlightUrl({ | |||||
id: data.id, | |||||
videoUrl: videoList | |||||
}).then(({ code }) => { | |||||
console.log(code) | |||||
videoUrl.value = arr.join(',') | |||||
videoId.value = videoIds | |||||
} | |||||
} | |||||
// 上传 - SRT | |||||
const vodStatusSRT = async(status) => { | |||||
srtUrl.value = status | |||||
console.log('srtUrl: ---------------', srtUrl.value, status) | |||||
// const res = await srtRefs.value.startUpload() | |||||
// console.log('status: ', status) | |||||
// console.log(res) | |||||
// if (status === 'success' && res.includes('error')) { | |||||
// $message.error('上传失败,请稍后重试') | |||||
// } else if (status === 'success') { | |||||
// srtUrl.value = res.join() | |||||
// } | |||||
} | |||||
// watchEffect(() => [videoUrl, srtUrl], ([videoVal], [srtVal]) => { | |||||
// console.log('watchEffect-videoVal:', videoVal) | |||||
// console.log('watchEffect-srtVal:', srtVal) | |||||
// if (videoUrl.value === 'success' && srtVal === 'success') { | |||||
// console.log('watchEffect-success all') | |||||
// isBtn.value = true | |||||
// } | |||||
// }) | |||||
// 上传 | |||||
const submitFile = () => { | |||||
if (data.photographyWay === 1) { // 视频 和 srt | |||||
// videoRefs.value.data.uploader.startUpload() | |||||
srtRefs.value?.startUpload().then(res => { | |||||
console.log('res:', res) | |||||
if (!res.includes('error')) { | |||||
uploadFlightUrl({ | |||||
id: data.id, | |||||
videoUrl: videoUrl.value, | |||||
videoId: videoId.value, | |||||
...res | |||||
}).then(({ code }) => { | |||||
if (code === 0) { | |||||
emit('close') | |||||
} | |||||
}) | |||||
} | |||||
}) | |||||
} else { | |||||
loading.value = true | |||||
const queue = [] | |||||
// queue.push(videoRefs.value?.data.uploader.startUpload()) | |||||
// queue.push(srtRefs.value?.startUpload()) | |||||
queue.push(inclinedRefs.value?.startUpload()) | |||||
queue.push(ortRefs.value?.startUpload()) | |||||
Promise.all(queue).then(res => { | |||||
loading.value = false | |||||
const params = {} | |||||
res.filter(it => it).forEach(el => { | |||||
Object.keys(el).map(dom => { | |||||
params[dom] = el[dom] | |||||
}) | |||||
}) | |||||
uploadFlightUrl({ | |||||
id: data.id, | |||||
...params | |||||
}).then(({ code }) => { | |||||
if (code === 0) { | |||||
emit('close') | |||||
} | |||||
}) | |||||
}) | }) | ||||
} | } | ||||
} | } | ||||
} | } | ||||
.btn { | .btn { | ||||
width: 70px; | |||||
margin-left: calc(50% - 30px); | |||||
width: 110px; | |||||
margin-left: calc(50% - 55px); | |||||
margin-top: 10px; | margin-top: 10px; | ||||
margin-bottom: 30px; | margin-bottom: 30px; | ||||
} | } |
<n-input v-if="item.type === 'inputStart'" v-model:value="userForm[item.key]" disabled v-bind="item.props"> | <n-input v-if="item.type === 'inputStart'" v-model:value="userForm[item.key]" disabled v-bind="item.props"> | ||||
<template #suffix> | <template #suffix> | ||||
<n-button quaternary type="info" @click="showStart = true">去标记 | |||||
<n-button quaternary type="info" style="padding-right: 0;" @click="showStart = true">去标记 | |||||
<n-icon size="20" color="rgba(42, 130, 228, 1)"> | <n-icon size="20" color="rgba(42, 130, 228, 1)"> | ||||
<LocationSharp /> | <LocationSharp /> | ||||
</n-icon> | </n-icon> | ||||
<n-input v-if="item.type === 'inputEnd'" v-model:value="userForm[item.key]" disabled v-bind="item.props"> | <n-input v-if="item.type === 'inputEnd'" v-model:value="userForm[item.key]" disabled v-bind="item.props"> | ||||
<template #suffix> | <template #suffix> | ||||
<n-button quaternary type="info" @click="showEnd = true">去标记 | |||||
<n-button quaternary type="info" style="padding-right: 0;" @click="showEnd = true">去标记 | |||||
<n-icon size="20" color="rgba(42, 130, 228, 1)"> | <n-icon size="20" color="rgba(42, 130, 228, 1)"> | ||||
<LocationSharp /> | <LocationSharp /> | ||||
</n-icon> | </n-icon> | ||||
type="datetime" | type="datetime" | ||||
value-format="yyyy-MM-dd HH:mm:ss" | value-format="yyyy-MM-dd HH:mm:ss" | ||||
:is-date-disabled="isDateDisabled" | :is-date-disabled="isDateDisabled" | ||||
style="width: 100%;" | |||||
/> | /> | ||||
<n-radio-group v-if="item.type === 'radio'" v-model:value="userForm[item.key]" :name="item.key"> | <n-radio-group v-if="item.type === 'radio'" v-model:value="userForm[item.key]" :name="item.key"> | ||||
<n-space> | <n-space> | ||||
height: 155px; | height: 155px; | ||||
margin-bottom: 10px; | margin-bottom: 10px; | ||||
} | } | ||||
:deep(.n-form-item-blank) { | |||||
width: 100%; | |||||
} | |||||
:deep(.__button-1jezgko-hlmmi) { | |||||
padding-right: 0; | |||||
} | |||||
</style> | </style> |
import { customRef } from 'vue' | import { customRef } from 'vue' | ||||
const status = ['任务待分配', '任务已分配', '飞手已接单', '任务飞行中', '任务已完成'] // 摄影方式 | |||||
const status = ['任务待分配', '任务已分配', '飞手已接单', '任务飞行中', '任务已完成'] | |||||
export default function(data) { | export default function(data) { | ||||
const list = [ | const list = [ |
<!-- 详情 - 抽屉 --> | <!-- 详情 - 抽屉 --> | ||||
<n-drawer v-model:show="showDraw" :width="'calc(100vw - 210px)'" :placement="'right'" resizable> | <n-drawer v-model:show="showDraw" :width="'calc(100vw - 210px)'" :placement="'right'" resizable> | ||||
<n-drawer-content closable> | <n-drawer-content closable> | ||||
<draw-comp :detail="detail" @close="showDraw = false" /> | |||||
<draw-comp :detail="detail" @close="showDraw = false;handleSearch()" /> | |||||
</n-drawer-content> | </n-drawer-content> | ||||
</n-drawer> | </n-drawer> | ||||
...toRefs(table), | ...toRefs(table), | ||||
...toRefs(search) | ...toRefs(search) | ||||
}) | }) | ||||
const status = ['任务待分配', '任务已分配', '飞手已接单', '任务飞行中', '任务已完成'] | |||||
const loadDataTable = async(res) => { | const loadDataTable = async(res) => { | ||||
const _params = { | const _params = { | ||||
...unref(data.searchParams), | ...unref(data.searchParams), | ||||
...res | ...res | ||||
} | } | ||||
return await getTaskList(_params) | |||||
const temp = await getTaskList(_params) | |||||
// 将状态由数字改成文字 | |||||
temp.data.records = temp.data.records.map(it => ({ | |||||
...it, | |||||
statusName: status[(it.status / 5) - 1] | |||||
})) | |||||
return temp | |||||
} | } | ||||
// 新增 | // 新增 | ||||
function handleModal() { | function handleModal() { |
import { h, ref, reactive } from 'vue' | import { h, ref, reactive } from 'vue' | ||||
import TableImage from '@/components/DataTable/tools/Image.vue' | |||||
import TableTags from '@/components/DataTable/tools/Tags.vue' | |||||
import TableSwitch from '@/components/DataTable/tools/Switch.vue' | |||||
import TableAction from '@/components/DataTable/tools/Action.vue' | import TableAction from '@/components/DataTable/tools/Action.vue' | ||||
import { resetPassword, deleteUser, setUserStatus } from '@/api/system/user/index.js' | |||||
import { getTaskInfo, taskDel } from '@/api/task' | import { getTaskInfo, taskDel } from '@/api/task' | ||||
/* 注册table */ | /* 注册table */ | ||||
data.showDraw = true | data.showDraw = true | ||||
} | } | ||||
/** | /** | ||||
* @description: 编辑 | * @description: 编辑 | ||||
* @return {*} | * @return {*} | ||||
{ | { | ||||
title: '巡逻地点', | title: '巡逻地点', | ||||
key: 'patrolLocation', | key: 'patrolLocation', | ||||
align: 'center', | |||||
align: 'center' | |||||
}, | }, | ||||
{ | { | ||||
title: '任务状态', | title: '任务状态', | ||||
key: 'status', | |||||
align: 'center', | |||||
key: 'statusName', | |||||
align: 'center' | |||||
}, | }, | ||||
{ | { | ||||
title: '飞手姓名', | title: '飞手姓名', | ||||
key: 'flightHandName', | key: 'flightHandName', | ||||
align: 'center', | |||||
align: 'center' | |||||
}, | }, | ||||
{ | { | ||||
title: '操作', | title: '操作', | ||||
props: { | props: { | ||||
type: 'primary', | type: 'primary', | ||||
text: true, | text: true, | ||||
onClick: editHandle.bind(null, row, 'update') | |||||
onClick: editHandle.bind(null, row, 'update'), | |||||
style: { | |||||
display: row.status > 10 ? 'none' : '' | |||||
} | |||||
}, | }, | ||||
auth: 'basic_list' | auth: 'basic_list' | ||||
}, | }, | ||||
}, | }, | ||||
ButtonProps: { | ButtonProps: { | ||||
text: true, | text: true, | ||||
type: 'primary' | |||||
type: 'primary', | |||||
style: { | |||||
display: row.status > 10 ? 'none' : '' | |||||
} | |||||
} | } | ||||
} | } | ||||
], | ], |
}, | }, | ||||
setup(props, { emit }) { | setup(props, { emit }) { | ||||
const MODAL_TYPE = { | const MODAL_TYPE = { | ||||
'create': '新建盒子信息', | |||||
'preview': '盒子详情', | |||||
'update': '编辑盒子信息' | |||||
'create': '新建人员信息', | |||||
'preview': '人员详情', | |||||
'update': '编辑人员信息' | |||||
} | } | ||||
const { userForm, userRules } = form | const { userForm, userRules } = form | ||||
const formRef = ref() | const formRef = ref() |
{ required: true, message: '请输入正确的联系电话', pattern: /^(0|86|17951)?(13[0-9]|15[012356789]|166|17[3678]|18[0-9]|14[57])[0-9]{8}$/, trigger: 'blur' } | { required: true, message: '请输入正确的联系电话', pattern: /^(0|86|17951)?(13[0-9]|15[012356789]|166|17[3678]|18[0-9]|14[57])[0-9]{8}$/, trigger: 'blur' } | ||||
], | ], | ||||
type: [{ required: true, type: 'number', message: '请选择身份', trigger: 'blur' }], | type: [{ required: true, type: 'number', message: '请选择身份', trigger: 'blur' }], | ||||
username: [{ required: true, message: '请输入帐号', trigger: 'blur' }], | |||||
password: [{ required: true, message: '请输入初始密码', trigger: 'blur' }] | |||||
username: [{ required: true, message: '账号为英文和数字', pattern: /^[a-zA-Z0-9]+$/, trigger: 'blur' }], | |||||
password: [{ required: true, message: '密码为6到20位的大小写英文和数字组成的', pattern: /^(?![\d]+$)(?![a-zA-Z]+$)(?![^\da-zA-Z]+$)([^\u4e00-\u9fa5\s]){6,20}$/, trigger: 'blur' }] | |||||
}, | }, | ||||
formItem: [ | formItem: [ | ||||
{ type: 'input', key: 'realname', label: '姓名', props: { maxlength: '20', placeholder: '请输入姓名', clearable: true }}, | |||||
{ type: 'input', key: 'realname', label: '姓名', props: { maxlength: '5', placeholder: '请输入姓名', clearable: true }}, | |||||
{ type: 'input', key: 'mobile', label: '联系电话', props: { maxlength: '20', placeholder: '请输入联系电话', clearable: true }}, | { type: 'input', key: 'mobile', label: '联系电话', props: { maxlength: '20', placeholder: '请输入联系电话', clearable: true }}, | ||||
{ type: 'select', key: 'type', label: '身份选择', props: { options: ROLE_TYPE, clearable: true }}, | { type: 'select', key: 'type', label: '身份选择', props: { options: ROLE_TYPE, clearable: true }}, | ||||
{ type: 'input', key: 'username', label: '帐号', props: { maxlength: '20', placeholder: '请输入帐号', clearable: true }}, | { type: 'input', key: 'username', label: '帐号', props: { maxlength: '20', placeholder: '请输入帐号', clearable: true }}, |
const data = reactive([ | const data = reactive([ | ||||
{ | { | ||||
label: '设备名称', | |||||
key: 'name', | |||||
label: '人员名称', | |||||
key: 'realname', | |||||
props: { | props: { | ||||
placeholder: '请输入设备名称' | |||||
placeholder: '请输入人员名称' | |||||
} | } | ||||
} | } | ||||
]) | ]) |