feat: 接入资产文件上传服务
This commit is contained in:
@@ -1,24 +0,0 @@
|
|||||||
import { AxiosProgressEvent } from 'axios'
|
|
||||||
import { request } from '@/api/request'
|
|
||||||
|
|
||||||
/** 上传文件 */
|
|
||||||
const FtsUpload = (data: FormData, onUploadProgress?: (progress: number) => void) => {
|
|
||||||
data.append('provider', 'local')
|
|
||||||
data.append('bucket', 'visual')
|
|
||||||
|
|
||||||
return request.post(`/Assets/v1/fts/uploader`, data, {
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'multipart/form-data',
|
|
||||||
},
|
|
||||||
onUploadProgress: onUploadProgress
|
|
||||||
? (progressEvent: AxiosProgressEvent) => {
|
|
||||||
if (progressEvent.total) {
|
|
||||||
const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total)
|
|
||||||
onUploadProgress(percentCompleted)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export default FtsUpload
|
|
||||||
@@ -1,5 +1,28 @@
|
|||||||
import { request } from '@/api/request'
|
import { request } from '@/api/request'
|
||||||
|
|
||||||
|
export interface Floor {
|
||||||
|
id?: number
|
||||||
|
name?: string
|
||||||
|
datacenter_id?: number
|
||||||
|
floor_number?: number
|
||||||
|
status?: string
|
||||||
|
area?: number
|
||||||
|
height?: number
|
||||||
|
load_bearing?: number
|
||||||
|
enabled?: boolean
|
||||||
|
layout_plan?: string
|
||||||
|
layout_plan_file_id?: string
|
||||||
|
description?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SaveFloorPayload = Omit<Floor, 'layout_plan'>
|
||||||
|
|
||||||
|
interface ApiResponse<T> {
|
||||||
|
code: number
|
||||||
|
message: string
|
||||||
|
details: T
|
||||||
|
}
|
||||||
|
|
||||||
/** 获取楼层列表(分页) */
|
/** 获取楼层列表(分页) */
|
||||||
export const fetchFloorList = (data?: { page?: number; page_size?: number; keyword?: string; datacenter_id?: number; status?: string }) => {
|
export const fetchFloorList = (data?: { page?: number; page_size?: number; keyword?: string; datacenter_id?: number; status?: string }) => {
|
||||||
return request.post('/Assets/v1/floor/list', data || {})
|
return request.post('/Assets/v1/floor/list', data || {})
|
||||||
@@ -16,13 +39,13 @@ export const fetchFloorListByDatacenter = (datacenterId: number, params?: { name
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 创建楼层 */
|
/** 创建楼层 */
|
||||||
export const createFloor = (data: any) => {
|
export const createFloor = (data: SaveFloorPayload) => {
|
||||||
return request.post('/Assets/v1/floor/create', data)
|
return request.post<ApiResponse<Floor>>('/Assets/v1/floor/create', data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 更新楼层 */
|
/** 更新楼层 */
|
||||||
export const updateFloor = (data: any) => {
|
export const updateFloor = (data: SaveFloorPayload) => {
|
||||||
return request.put('/Assets/v1/floor/update', data)
|
return request.put<ApiResponse<Floor>>('/Assets/v1/floor/update', data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 删除楼层 */
|
/** 删除楼层 */
|
||||||
|
|||||||
48
src/api/ops/floorFile.ts
Normal file
48
src/api/ops/floorFile.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { request } from '@/api/request'
|
||||||
|
|
||||||
|
export interface PendingUpload {
|
||||||
|
file_id: string
|
||||||
|
object_key: string
|
||||||
|
upload: {
|
||||||
|
method: string
|
||||||
|
url: string
|
||||||
|
headers: Record<string, string>
|
||||||
|
expires_at: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ApiResponse<T> {
|
||||||
|
code: number
|
||||||
|
message: string
|
||||||
|
details: T
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 初始化楼层布局文件并直传 OSS。 */
|
||||||
|
export async function uploadFloorLayout(file: File): Promise<PendingUpload> {
|
||||||
|
const response = await request.post<ApiResponse<PendingUpload>>('/Assets/v1/floor/layout/init', {
|
||||||
|
filename: file.name,
|
||||||
|
size: file.size,
|
||||||
|
content_type: file.type || 'application/octet-stream',
|
||||||
|
})
|
||||||
|
|
||||||
|
if (response.code !== 0) {
|
||||||
|
throw new Error(response.message || '初始化楼层布局文件上传失败')
|
||||||
|
}
|
||||||
|
|
||||||
|
const pendingUpload = response.details
|
||||||
|
if (!pendingUpload?.file_id || !pendingUpload.upload?.method || !pendingUpload.upload?.url) {
|
||||||
|
throw new Error('楼层布局文件上传参数不完整')
|
||||||
|
}
|
||||||
|
|
||||||
|
const uploadResponse = await fetch(pendingUpload.upload.url, {
|
||||||
|
method: pendingUpload.upload.method,
|
||||||
|
headers: pendingUpload.upload.headers,
|
||||||
|
body: file,
|
||||||
|
credentials: 'omit',
|
||||||
|
})
|
||||||
|
if (!uploadResponse.ok) {
|
||||||
|
throw new Error(`上传楼层布局文件失败: HTTP ${uploadResponse.status}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return pendingUpload
|
||||||
|
}
|
||||||
@@ -23,17 +23,6 @@ export interface GoViewProjectData {
|
|||||||
createUserId: string
|
createUserId: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GoViewFile {
|
|
||||||
id: string
|
|
||||||
fileName: string
|
|
||||||
fileSize: number
|
|
||||||
fileSuffix: string
|
|
||||||
virtualKey: string
|
|
||||||
relativePath: string
|
|
||||||
absolutePath: string
|
|
||||||
createTime: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface OssInfo {
|
export interface OssInfo {
|
||||||
BucketName: string
|
BucketName: string
|
||||||
bucketURL: string
|
bucketURL: string
|
||||||
@@ -106,15 +95,6 @@ export const publishProject = (data: { identity: string; state: -1 | 1 }) => {
|
|||||||
return request.put<GoViewResponse<{ identity: string; state: number }>>('/Visual/v1/project/publish', data)
|
return request.put<GoViewResponse<{ identity: string; state: number }>>('/Visual/v1/project/publish', data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 上传文件 */
|
|
||||||
export const uploadFile = (file: File) => {
|
|
||||||
const formData = new FormData()
|
|
||||||
formData.append('object', file)
|
|
||||||
return request.post<GoViewResponse<GoViewFile>>('/Visual/v1/project/upload', formData, {
|
|
||||||
headers: { 'Content-Type': 'multipart/form-data' },
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 保存项目数据 */
|
/** 保存项目数据 */
|
||||||
export const saveProjectData = (projectId: string, content: string) => {
|
export const saveProjectData = (projectId: string, content: string) => {
|
||||||
const formData = new URLSearchParams()
|
const formData = new URLSearchParams()
|
||||||
|
|||||||
@@ -60,12 +60,6 @@
|
|||||||
list-type="picture-card"
|
list-type="picture-card"
|
||||||
accept="image/*"
|
accept="image/*"
|
||||||
>
|
>
|
||||||
<!-- <template #upload-button>
|
|
||||||
<div class="upload-btn">
|
|
||||||
<icon-plus />
|
|
||||||
<div class="upload-text">上传图片</div>
|
|
||||||
</div>
|
|
||||||
</template> -->
|
|
||||||
</a-upload>
|
</a-upload>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
@@ -77,27 +71,11 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, watch, onMounted } from 'vue'
|
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
||||||
import { computed } from 'vue'
|
|
||||||
import { Message } from '@arco-design/web-vue'
|
import { Message } from '@arco-design/web-vue'
|
||||||
import { IconPlus } from '@arco-design/web-vue/es/icon'
|
import { createFloor, fetchDatacenterList, updateFloor } from '@/api/ops/floor'
|
||||||
import { createFloor, updateFloor } from '@/api/ops/floor'
|
import type { Floor, SaveFloorPayload } from '@/api/ops/floor'
|
||||||
import { fetchDatacenterList } from '@/api/ops/floor'
|
import { uploadFloorLayout } from '@/api/ops/floorFile'
|
||||||
import FtsUpload from '@/api/common/fts'
|
|
||||||
|
|
||||||
interface Floor {
|
|
||||||
id?: number
|
|
||||||
name?: string
|
|
||||||
datacenter_id?: number
|
|
||||||
floor_number?: number
|
|
||||||
status?: string
|
|
||||||
area?: number
|
|
||||||
height?: number
|
|
||||||
load_bearing?: number
|
|
||||||
enabled?: boolean
|
|
||||||
layout_plan?: string
|
|
||||||
description?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
visible: boolean
|
visible: boolean
|
||||||
@@ -117,6 +95,7 @@ const loadingDatacenters = ref(false)
|
|||||||
const submitting = ref(false)
|
const submitting = ref(false)
|
||||||
const datacenterList = ref<any[]>([])
|
const datacenterList = ref<any[]>([])
|
||||||
const fileList = ref<any[]>([])
|
const fileList = ref<any[]>([])
|
||||||
|
const localPreviewURL = ref('')
|
||||||
|
|
||||||
// 表单数据
|
// 表单数据
|
||||||
const form = ref({
|
const form = ref({
|
||||||
@@ -129,12 +108,20 @@ const form = ref({
|
|||||||
load_bearing: undefined as number | undefined,
|
load_bearing: undefined as number | undefined,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
layout_plan: '',
|
layout_plan: '',
|
||||||
|
layout_plan_file_id: '',
|
||||||
description: '',
|
description: '',
|
||||||
})
|
})
|
||||||
|
|
||||||
// 是否为编辑模式
|
// 是否为编辑模式
|
||||||
const isEdit = computed(() => !!props.floor?.id)
|
const isEdit = computed(() => !!props.floor?.id)
|
||||||
|
|
||||||
|
const releaseLocalPreviewURL = () => {
|
||||||
|
if (localPreviewURL.value) {
|
||||||
|
URL.revokeObjectURL(localPreviewURL.value)
|
||||||
|
localPreviewURL.value = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 加载数据中心列表
|
// 加载数据中心列表
|
||||||
const loadDatacenterList = async () => {
|
const loadDatacenterList = async () => {
|
||||||
loadingDatacenters.value = true
|
loadingDatacenters.value = true
|
||||||
@@ -153,22 +140,16 @@ const loadDatacenterList = async () => {
|
|||||||
// 处理文件上传
|
// 处理文件上传
|
||||||
const handleUpload = async (option: any) => {
|
const handleUpload = async (option: any) => {
|
||||||
try {
|
try {
|
||||||
const file = option.fileItem.file
|
const file = option.fileItem.file as File
|
||||||
const formData = new FormData()
|
const pendingUpload = await uploadFloorLayout(file)
|
||||||
formData.append('file', file)
|
|
||||||
|
|
||||||
// 使用fts接口上传文件
|
releaseLocalPreviewURL()
|
||||||
const res: any = await FtsUpload(formData)
|
localPreviewURL.value = URL.createObjectURL(file)
|
||||||
|
option.fileItem.url = localPreviewURL.value
|
||||||
if (res.code === 0) {
|
form.value.layout_plan = ''
|
||||||
// 上传成功,设置文件URL
|
form.value.layout_plan_file_id = pendingUpload.file_id
|
||||||
form.value.layout_plan = res.data?.result_url || ''
|
option.onSuccess(pendingUpload)
|
||||||
option.onSuccess(res)
|
Message.success('上传成功')
|
||||||
Message.success('上传成功')
|
|
||||||
} else {
|
|
||||||
option.onError(new Error('上传失败'))
|
|
||||||
Message.error('上传失败')
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('文件上传失败:', error)
|
console.error('文件上传失败:', error)
|
||||||
option.onError(error)
|
option.onError(error)
|
||||||
@@ -179,6 +160,11 @@ const handleUpload = async (option: any) => {
|
|||||||
// 处理文件变化
|
// 处理文件变化
|
||||||
const handleFileChange = (files: any[]) => {
|
const handleFileChange = (files: any[]) => {
|
||||||
fileList.value = files
|
fileList.value = files
|
||||||
|
if (files.length === 0) {
|
||||||
|
releaseLocalPreviewURL()
|
||||||
|
form.value.layout_plan = ''
|
||||||
|
form.value.layout_plan_file_id = ''
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 监听对话框显示状态
|
// 监听对话框显示状态
|
||||||
@@ -186,6 +172,7 @@ watch(
|
|||||||
() => props.visible,
|
() => props.visible,
|
||||||
(newVal) => {
|
(newVal) => {
|
||||||
if (newVal) {
|
if (newVal) {
|
||||||
|
releaseLocalPreviewURL()
|
||||||
if (props.floor && isEdit.value) {
|
if (props.floor && isEdit.value) {
|
||||||
// 编辑模式:填充表单
|
// 编辑模式:填充表单
|
||||||
form.value = {
|
form.value = {
|
||||||
@@ -198,6 +185,7 @@ watch(
|
|||||||
load_bearing: props.floor.load_bearing,
|
load_bearing: props.floor.load_bearing,
|
||||||
enabled: props.floor.enabled !== undefined ? props.floor.enabled : true,
|
enabled: props.floor.enabled !== undefined ? props.floor.enabled : true,
|
||||||
layout_plan: props.floor.layout_plan || '',
|
layout_plan: props.floor.layout_plan || '',
|
||||||
|
layout_plan_file_id: props.floor.layout_plan_file_id || '',
|
||||||
description: props.floor.description || '',
|
description: props.floor.description || '',
|
||||||
}
|
}
|
||||||
// 如果有布局图,设置文件列表
|
// 如果有布局图,设置文件列表
|
||||||
@@ -224,11 +212,12 @@ watch(
|
|||||||
load_bearing: undefined,
|
load_bearing: undefined,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
layout_plan: '',
|
layout_plan: '',
|
||||||
|
layout_plan_file_id: '',
|
||||||
description: '',
|
description: '',
|
||||||
}
|
}
|
||||||
fileList.value = []
|
fileList.value = []
|
||||||
}
|
}
|
||||||
}
|
} else releaseLocalPreviewURL()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -239,7 +228,7 @@ const handleOk = async () => {
|
|||||||
|
|
||||||
submitting.value = true
|
submitting.value = true
|
||||||
try {
|
try {
|
||||||
const data: any = {
|
const data: SaveFloorPayload = {
|
||||||
name: form.value.name,
|
name: form.value.name,
|
||||||
datacenter_id: form.value.datacenter_id,
|
datacenter_id: form.value.datacenter_id,
|
||||||
floor_number: form.value.floor_number,
|
floor_number: form.value.floor_number,
|
||||||
@@ -248,7 +237,7 @@ const handleOk = async () => {
|
|||||||
height: form.value.height,
|
height: form.value.height,
|
||||||
load_bearing: form.value.load_bearing,
|
load_bearing: form.value.load_bearing,
|
||||||
enabled: form.value.enabled,
|
enabled: form.value.enabled,
|
||||||
layout_plan: form.value.layout_plan,
|
layout_plan_file_id: form.value.layout_plan_file_id,
|
||||||
description: form.value.description,
|
description: form.value.description,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,6 +252,9 @@ const handleOk = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
|
const savedFloor = res.details
|
||||||
|
form.value.layout_plan = savedFloor?.layout_plan || ''
|
||||||
|
form.value.layout_plan_file_id = savedFloor?.layout_plan_file_id || ''
|
||||||
Message.success(isEdit.value ? '编辑成功' : '创建成功')
|
Message.success(isEdit.value ? '编辑成功' : '创建成功')
|
||||||
emit('success')
|
emit('success')
|
||||||
emit('update:visible', false)
|
emit('update:visible', false)
|
||||||
@@ -279,11 +271,13 @@ const handleOk = async () => {
|
|||||||
|
|
||||||
// 取消
|
// 取消
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
|
releaseLocalPreviewURL()
|
||||||
emit('update:visible', false)
|
emit('update:visible', false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理对话框可见性变化
|
// 处理对话框可见性变化
|
||||||
const handleVisibleChange = (visible: boolean) => {
|
const handleVisibleChange = (visible: boolean) => {
|
||||||
|
if (!visible) releaseLocalPreviewURL()
|
||||||
emit('update:visible', visible)
|
emit('update:visible', visible)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,6 +285,8 @@ const handleVisibleChange = (visible: boolean) => {
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadDatacenterList()
|
loadDatacenterList()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(releaseLocalPreviewURL)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
@@ -298,19 +294,3 @@ export default {
|
|||||||
name: 'FloorFormDialog',
|
name: 'FloorFormDialog',
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less">
|
|
||||||
.upload-btn {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.upload-text {
|
|
||||||
margin-top: 8px;
|
|
||||||
font-size: 12px;
|
|
||||||
color: var(--color-text-2);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
Reference in New Issue
Block a user