|
@@ -1,34 +1,38 @@
|
|
|
<script setup lang="ts">
|
|
|
-import { nextTick, reactive, ref } from 'vue'
|
|
|
-import type { FormRules } from 'element-plus'
|
|
|
-
|
|
|
-import axiosInstance from '@/utils/axios/axiosInstance.ts'
|
|
|
-import { useRequest } from '@/hooks/useRequest.ts'
|
|
|
+import { MaterialAPI } from '@/config/API.ts'
|
|
|
+import { useFileUpload } from '@/hooks/File/useFileUpload.ts'
|
|
|
import type { ResponseInfo } from '@/types/axios.ts'
|
|
|
|
|
|
-const { AllApi } = useRequest()
|
|
|
-
|
|
|
-interface UploadAssetForm {
|
|
|
- name: string
|
|
|
- tag: Array<string>
|
|
|
- gid: string
|
|
|
- filePath: string
|
|
|
- md5: string
|
|
|
-}
|
|
|
-
|
|
|
-interface GameInfo {
|
|
|
- gid: string
|
|
|
- label: string
|
|
|
-}
|
|
|
+import axiosInstance from '@/utils/axios/axiosInstance.ts'
|
|
|
+import type {
|
|
|
+ AddTagRes,
|
|
|
+ GameData,
|
|
|
+ GameSelectItem,
|
|
|
+ ResFileInfo,
|
|
|
+ TagItem,
|
|
|
+ UploadAssetForm,
|
|
|
+} from '@/views/Material/types/uploadFileType.ts'
|
|
|
+import { Plus } from '@element-plus/icons-vue'
|
|
|
+import type {
|
|
|
+ FormInstance,
|
|
|
+ FormRules,
|
|
|
+ UploadFile,
|
|
|
+ UploadFiles,
|
|
|
+ UploadInstance,
|
|
|
+ UploadRawFile,
|
|
|
+ UploadRequestOptions,
|
|
|
+ UploadUserFile,
|
|
|
+} from 'element-plus'
|
|
|
+import { ElMessageBox, genFileId } from 'element-plus'
|
|
|
+import { nextTick, onMounted, reactive, ref, toRaw } from 'vue'
|
|
|
|
|
|
-interface TagInfo {
|
|
|
- value: string
|
|
|
-}
|
|
|
+const { isValidFile, handleFile, getAllTags } = useFileUpload()
|
|
|
|
|
|
const uploadForm = reactive<UploadAssetForm>({
|
|
|
name: '',
|
|
|
- tag: [],
|
|
|
+ tags: [],
|
|
|
gid: '',
|
|
|
+ pid: '',
|
|
|
filePath: '',
|
|
|
md5: '',
|
|
|
})
|
|
@@ -38,61 +42,228 @@ const rules = reactive<FormRules<UploadAssetForm>>({
|
|
|
{ required: true, message: '请输入创意名', trigger: 'blur' },
|
|
|
{ min: 3, max: 5, message: '3-5个字符', trigger: 'blur' },
|
|
|
],
|
|
|
- tag: [{ required: true, message: '请选择标签', trigger: 'blur' }],
|
|
|
- gid: [{ required: true, message: '请选择游戏', trigger: 'blur' }],
|
|
|
+ tags: [
|
|
|
+ { required: true, message: '请选择标签', trigger: 'change' },
|
|
|
+ { required: true, message: '请选择标签', trigger: 'blur' },
|
|
|
+ ],
|
|
|
+ gid: [{ required: true, message: '请选择游戏', trigger: 'change' }],
|
|
|
+ filePath: [{ required: true, message: '请选择素材', trigger: 'change' }],
|
|
|
})
|
|
|
|
|
|
-const gameInfoList = reactive<Array<GameInfo>>([
|
|
|
- {
|
|
|
- gid: '1',
|
|
|
- label: '游戏1',
|
|
|
- },
|
|
|
- {
|
|
|
- gid: '2',
|
|
|
- label: '游戏12',
|
|
|
- },
|
|
|
- {
|
|
|
- gid: '3',
|
|
|
- label: '游戏13',
|
|
|
- },
|
|
|
-])
|
|
|
+let gameInfoList: GameData = {}
|
|
|
+const gameSelect = reactive<GameSelectItem[]>([])
|
|
|
|
|
|
+const uploadRef = ref<UploadInstance>()
|
|
|
+const fileFormRef = ref<FormInstance>()
|
|
|
const tagInput = ref('')
|
|
|
const autocompleteRef = ref()
|
|
|
+const isVideo = ref(false)
|
|
|
+const limit = 1
|
|
|
+const allTags: Array<TagItem> = []
|
|
|
|
|
|
-const tagSearch = async (queryString: string, cb: any) => {
|
|
|
- const resInfo = (await axiosInstance.get(AllApi.tags)) as ResponseInfo
|
|
|
- if (resInfo.code !== 0) {
|
|
|
- return []
|
|
|
+const cascadePropsConfig = {
|
|
|
+ expandTrigger: 'hover' as const,
|
|
|
+}
|
|
|
+
|
|
|
+const gameSelectChange = (value: string[]) => {
|
|
|
+ uploadForm.pid = value[0]
|
|
|
+ uploadForm.gid = value[1]
|
|
|
+}
|
|
|
+
|
|
|
+const generateGameSelect = () => {
|
|
|
+ gameSelect.splice(0, gameSelect.length)
|
|
|
+ for (let [k, v] of Object.entries(gameInfoList)) {
|
|
|
+ const children = v.map(item => {
|
|
|
+ return {
|
|
|
+ value: item.gid,
|
|
|
+ label: item.gameName,
|
|
|
+ }
|
|
|
+ })
|
|
|
+ gameSelect.push({
|
|
|
+ value: k,
|
|
|
+ label: k === '' ? '默认' : k,
|
|
|
+ children,
|
|
|
+ })
|
|
|
}
|
|
|
- const results = resInfo.data as Array<TagInfo>
|
|
|
+}
|
|
|
|
|
|
+const tagSearch = async (queryString: string, cb: any) => {
|
|
|
const result = queryString
|
|
|
- ? results.filter(createFilter(queryString))
|
|
|
- : results
|
|
|
+ ? allTags.filter(createFilter(queryString))
|
|
|
+ : allTags
|
|
|
|
|
|
cb(result)
|
|
|
}
|
|
|
|
|
|
const createFilter = (queryString: string) => {
|
|
|
- return (restaurant: TagInfo) => {
|
|
|
+ return (restaurant: TagItem) => {
|
|
|
return (
|
|
|
- restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0
|
|
|
+ restaurant.name.toLowerCase().indexOf(queryString.toLowerCase()) === 0
|
|
|
)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-const handleTagSelect = (tag: TagInfo) => {
|
|
|
- uploadForm.tag.push(tag.value)
|
|
|
- tagInput.value = ''
|
|
|
+const handleTagSelect = (tags: TagItem) => {
|
|
|
+ uploadForm.tags.push(tags.id)
|
|
|
nextTick(() => {
|
|
|
autocompleteRef.value.blur()
|
|
|
})
|
|
|
}
|
|
|
|
|
|
-const goBack = () => {
|
|
|
- console.log('back')
|
|
|
+const handleExceed = async (
|
|
|
+ files: UploadRawFile[],
|
|
|
+ fileList: UploadUserFile[],
|
|
|
+) => {
|
|
|
+ const confirm = await ElMessageBox.confirm(
|
|
|
+ '上传将覆盖前一个文件,是否继续上传?',
|
|
|
+ {
|
|
|
+ confirmButtonText: '继续上传',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ type: 'warning',
|
|
|
+ },
|
|
|
+ )
|
|
|
+ const file = files[0]
|
|
|
+ if (!confirm) return false
|
|
|
+ handleFile(file, fileList, isVideo, uploadForm)
|
|
|
+ nextTick(() => {
|
|
|
+ fileFormRef.value?.validateField('filePath')
|
|
|
+ })
|
|
|
+ uploadRef.value!.clearFiles()
|
|
|
+ file.uid = genFileId()
|
|
|
+ uploadRef.value!.handleStart(file)
|
|
|
}
|
|
|
+
|
|
|
+const handleFileChange = (file: UploadFile, fileList: UploadFiles) => {
|
|
|
+ if (!file || !file.raw) {
|
|
|
+ ElMessage.warning('请传入合法文件')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (!isValidFile(file)) {
|
|
|
+ fileList.splice(1)
|
|
|
+ ElMessage.warning('上传文件超出限制或不是规定文件')
|
|
|
+ }
|
|
|
+
|
|
|
+ handleFile(file, fileList, isVideo, uploadForm)
|
|
|
+ nextTick(() => {
|
|
|
+ fileFormRef.value?.validateField('filePath')
|
|
|
+ })
|
|
|
+ // 在第一次上传成功后,文件状态会被设置为success,此时是无法重新提交文件上传的,故需要手动重置
|
|
|
+ fileList.forEach(file => {
|
|
|
+ file.status = 'ready'
|
|
|
+ })
|
|
|
+
|
|
|
+ console.log(uploadForm)
|
|
|
+}
|
|
|
+
|
|
|
+const submitAsset = async (formEl: FormInstance | undefined) => {
|
|
|
+ if (!formEl) return
|
|
|
+ const isValid = await formEl.validate()
|
|
|
+ if (!isValid) {
|
|
|
+ ElMessage.warning('请填写完整信息')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ uploadRef.value?.submit()
|
|
|
+}
|
|
|
+
|
|
|
+const submitForm = async () => {
|
|
|
+ const url = isVideo.value ? MaterialAPI.videoSubmit : MaterialAPI.imgSubmit
|
|
|
+ const nFormData = JSON.parse(JSON.stringify(toRaw(uploadForm)))
|
|
|
+ if (isVideo.value) {
|
|
|
+ nFormData.videoPath = nFormData.filePath
|
|
|
+ } else {
|
|
|
+ nFormData.imgPath = nFormData.filePath
|
|
|
+ }
|
|
|
+ delete nFormData.filePath
|
|
|
+
|
|
|
+ // TODO 完善图片和视频的返回值类型
|
|
|
+ const result = await axiosInstance.post(url, nFormData)
|
|
|
+ if (result.code !== 0) {
|
|
|
+ ElMessage.error('上传素材失败')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ ElMessage.success('上传素材成功')
|
|
|
+ uploadRef.value?.clearFiles()
|
|
|
+ tagInput.value = ''
|
|
|
+ fileFormRef.value?.resetFields()
|
|
|
+}
|
|
|
+
|
|
|
+const uploadFile = async (options: UploadRequestOptions) => {
|
|
|
+ if (!options || !options.file) {
|
|
|
+ ElMessage.warning('请先上传文件')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const file = options.file
|
|
|
+ const formData = new FormData()
|
|
|
+ formData.append('file', file)
|
|
|
+ const result = (await axiosInstance.postForm(
|
|
|
+ MaterialAPI.uploadAsset,
|
|
|
+ formData,
|
|
|
+ )) as ResFileInfo
|
|
|
+ if (result.code !== 0) {
|
|
|
+ ElMessage.error('上传素材失败')
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ const { md5, path } = result
|
|
|
+ uploadForm.md5 = md5
|
|
|
+ uploadForm.filePath = path
|
|
|
+ await submitForm()
|
|
|
+}
|
|
|
+
|
|
|
+const goBack = () => {}
|
|
|
+
|
|
|
+const tagAdd = async (val: string) => {
|
|
|
+ const isExist = allTags.find(item => item.name === val)
|
|
|
+ const newTagName = val
|
|
|
+ uploadForm.tags.splice(uploadForm.tags.length - 1, 1)
|
|
|
+ let id = -1
|
|
|
+ if (!isExist) {
|
|
|
+ const confirm = await ElMessageBox.confirm('不存在改标签,是否创建?', {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ type: 'warning',
|
|
|
+ })
|
|
|
+ if (!confirm) return
|
|
|
+ const res = (await axiosInstance.post(MaterialAPI.addTag, {
|
|
|
+ name: newTagName,
|
|
|
+ })) as AddTagRes
|
|
|
+ if (res.code !== 0) {
|
|
|
+ ElMessage.error('创建标签失败')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ ElMessage.success('创建标签成功')
|
|
|
+
|
|
|
+ await updateTags()
|
|
|
+ id = res.data.id
|
|
|
+ } else {
|
|
|
+ id = isExist.id
|
|
|
+ }
|
|
|
+ uploadForm.tags.push(id)
|
|
|
+}
|
|
|
+
|
|
|
+const clearTagInput = () => {
|
|
|
+ uploadForm.tags = []
|
|
|
+}
|
|
|
+
|
|
|
+const updateTags = async () => {
|
|
|
+ const tags = await getAllTags()
|
|
|
+ allTags.splice(0, allTags.length, ...tags)
|
|
|
+}
|
|
|
+
|
|
|
+const updateGameInfo = async () => {
|
|
|
+ const res = (await axiosInstance.post(
|
|
|
+ MaterialAPI.getAllGamesInfo,
|
|
|
+ {},
|
|
|
+ )) as ResponseInfo
|
|
|
+ console.log(res)
|
|
|
+ gameInfoList = res.data as GameData
|
|
|
+ generateGameSelect()
|
|
|
+}
|
|
|
+
|
|
|
+onMounted(async () => {
|
|
|
+ await updateTags()
|
|
|
+ await updateGameInfo()
|
|
|
+})
|
|
|
</script>
|
|
|
|
|
|
<template>
|
|
@@ -109,6 +280,7 @@ const goBack = () => {
|
|
|
:rules="rules"
|
|
|
:model="uploadForm"
|
|
|
:label-position="'left'"
|
|
|
+ ref="fileFormRef"
|
|
|
label-width="auto"
|
|
|
class="uploadForm"
|
|
|
>
|
|
@@ -120,36 +292,47 @@ const goBack = () => {
|
|
|
/>
|
|
|
</el-form-item>
|
|
|
<el-form-item label="游戏" prop="gid">
|
|
|
- <el-select
|
|
|
- class="w220"
|
|
|
+ <el-cascader
|
|
|
v-model="uploadForm.gid"
|
|
|
- placeholder="选择游戏"
|
|
|
- >
|
|
|
- <el-option
|
|
|
- v-for="item in gameInfoList"
|
|
|
- :label="item.label"
|
|
|
- :value="item.gid"
|
|
|
- />
|
|
|
- </el-select>
|
|
|
+ :options="gameSelect"
|
|
|
+ :props="cascadePropsConfig"
|
|
|
+ @change="gameSelectChange"
|
|
|
+ />
|
|
|
</el-form-item>
|
|
|
- <el-form-item label="标签" prop="tag">
|
|
|
+ <el-form-item label="标签" prop="tags">
|
|
|
<div class="tagContainer">
|
|
|
+ <!-- 这里需要修改value-key,否则会导致警告 -->
|
|
|
<el-autocomplete
|
|
|
:debounce="200"
|
|
|
v-model="tagInput"
|
|
|
+ value-key="name"
|
|
|
:fetch-suggestions="tagSearch"
|
|
|
clearable
|
|
|
class="inline-input w220"
|
|
|
placeholder="请输入标签以搜索"
|
|
|
@select="handleTagSelect"
|
|
|
ref="autocompleteRef"
|
|
|
- />
|
|
|
+ >
|
|
|
+ <template #default="{ item }">
|
|
|
+ <div class="value">{{ item.name }}</div>
|
|
|
+ </template>
|
|
|
+ </el-autocomplete>
|
|
|
<el-input-tag
|
|
|
clearable
|
|
|
class="inputTag"
|
|
|
- v-model="uploadForm.tag"
|
|
|
+ v-model="uploadForm.tags"
|
|
|
+ tag-type="primary"
|
|
|
+ tag-effect="plain"
|
|
|
+ @add-tag="tagAdd"
|
|
|
+ @clear="clearTagInput"
|
|
|
placeholder="如需新建标签,请直接在此处输入后回车"
|
|
|
- />
|
|
|
+ >
|
|
|
+ <template #tag="{ value }">
|
|
|
+ <span>{{
|
|
|
+ allTags.find(item => item.id === value)?.name
|
|
|
+ }}</span>
|
|
|
+ </template>
|
|
|
+ </el-input-tag>
|
|
|
</div>
|
|
|
</el-form-item>
|
|
|
<el-form-item label="文件" prop="filePath">
|
|
@@ -157,39 +340,40 @@ const goBack = () => {
|
|
|
<el-upload
|
|
|
class="uploadContainer"
|
|
|
drag
|
|
|
- multiple
|
|
|
:auto-upload="false"
|
|
|
list-type="picture"
|
|
|
+ :limit="limit"
|
|
|
+ :on-change="handleFileChange"
|
|
|
+ :on-exceed="handleExceed"
|
|
|
+ ref="uploadRef"
|
|
|
+ :http-request="uploadFile"
|
|
|
>
|
|
|
<el-icon class="el-icon--upload">
|
|
|
<upload-filled />
|
|
|
</el-icon>
|
|
|
<div class="el-upload__text">
|
|
|
<p>将文件或文件夹拖到此处,或点击上传文件</p>
|
|
|
+ <p class="upload-tips">1.仅支持图片和视频文件,单个上传</p>
|
|
|
<p class="upload-tips">
|
|
|
- 1.仅支持图片和视频文件,上传添加不超过500个
|
|
|
- </p>
|
|
|
- <p class="upload-tips">
|
|
|
- 2.支持图片格式:png、jpg、jpeg、gif,不超过100MB;支持视频格式:mp4、mpeg、3pg、avi、mov,不超过5G
|
|
|
+ 2.支持图片格式:png、jpg、jpeg,不超过100MB;支持视频格式:mp4、avi,不超过100MB
|
|
|
</p>
|
|
|
<p class="upload-tips">
|
|
|
3.素材上传后需对尺寸、码率等进行分析,约1-3分钟后方可用于投放
|
|
|
</p>
|
|
|
</div>
|
|
|
- <template #file="{ file }">
|
|
|
- <div>
|
|
|
- <img
|
|
|
- class="el-upload-list__item-thumbnail"
|
|
|
- :src="file.url"
|
|
|
- alt=""
|
|
|
- />
|
|
|
- <div>撒都</div>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
</el-upload>
|
|
|
</div>
|
|
|
</el-form-item>
|
|
|
</el-form>
|
|
|
+ <div class="fileFooter">
|
|
|
+ <el-button
|
|
|
+ color="#197AFB"
|
|
|
+ :icon="Plus"
|
|
|
+ class="el-button-mini"
|
|
|
+ @click="submitAsset(fileFormRef)"
|
|
|
+ >确定
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
@@ -254,4 +438,8 @@ const goBack = () => {
|
|
|
.upload-tips {
|
|
|
color: #b6b7b7;
|
|
|
}
|
|
|
+
|
|
|
+.fileItem {
|
|
|
+ display: flex;
|
|
|
+}
|
|
|
</style>
|