Skip to content

Commit

Permalink
Upload ext (#549)
Browse files Browse the repository at this point in the history
* feat: 添加资源按钮管理功能及组件支持

* feat: 增强资源按钮交互及上传功能

* feat: 改进资源上传接口及事件处理逻辑
  • Loading branch information
kanyxmo authored Feb 21, 2025
1 parent 88a7200 commit a8f2b26
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 38 deletions.
73 changes: 40 additions & 33 deletions web/src/components/ma-resource-picker/panel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ import { ResultCode } from '@/utils/ResultCode.ts'
import { useImageViewer } from '@/hooks/useImageViewer.ts'
import { useMessage } from '@/hooks/useMessage.ts'
import type { TransType } from '@/hooks/auto-imports/useTrans.ts'
import type { Resources } from '#/global'
import useParentNode from '@/hooks/useParentNode.ts'

defineOptions({ name: 'MaResourcePanel' })

Expand All @@ -93,6 +95,7 @@ const i18n = useTrans() as TransType
const t = i18n.globalTrans

const msg = useMessage()
const resourceStore = useResourceStore()

const modelValue = defineModel<string | string[] | undefined>()

Expand Down Expand Up @@ -362,13 +365,29 @@ function executeContextmenu(e: MouseEvent, resource: Resource) {
})
}

function handleFile(ev: InputEvent, btn: Resources.Button) {
btn.upload?.((ev?.target!.files) as FileList, { btn, getResourceList })
}

onMounted(async () => {
await getResourceList()

const apps = document.getElementsByClassName('res-app') as HTMLCollectionOf<HTMLDivElement>

for (let i = 0; i < apps.length; i++) {
const app = apps[i] as HTMLDivElement
app.addEventListener('click', (e: MouseEvent) => {
e.stopPropagation()
const node = useParentNode(e, 'div')
const fileInput = node.children[0]
const btn = resourceStore.getAllButton()?.find(item => item.name === fileInput.getAttribute('name'))
if (btn?.click) {
btn?.click?.(btn, selected as any)
}
if (btn?.upload) {
fileInput?.click?.()
}
})
app?.parentElement?.addEventListener('mouseover', () => {
const index = i
app.className = 'res-app main-effect'
Expand Down Expand Up @@ -420,6 +439,7 @@ onUnmounted(() => {
document.querySelectorAll('.ma-resource-dock .res-app').forEach((app) => {
app.removeEventListener('mousemove', () => {})
app.removeEventListener('mouseout', () => {})
app.removeEventListener('click', () => {})
})
})
</script>
Expand Down Expand Up @@ -549,38 +569,25 @@ onUnmounted(() => {
</div>
</div>

<!-- <div class="ma-resource-dock"> -->
<!-- <div class="res-app-container"> -->
<!-- <div class="res-app"> -->
<!-- <ma-svg-icon name="solar:upload-linear" class="res-app-icon" /> -->
<!-- </div> -->
<!-- </div> -->
<!-- <div class="res-app-container"> -->
<!-- <div class="res-app"> -->
<!-- <ma-svg-icon name="solar:upload-linear" class="res-app-icon" /> -->
<!-- </div> -->
<!-- </div> -->
<!-- <div class="res-app-container"> -->
<!-- <div class="res-app"> -->
<!-- <ma-svg-icon name="solar:upload-linear" class="res-app-icon" /> -->
<!-- </div> -->
<!-- </div> -->
<!-- <div class="res-app-container"> -->
<!-- <div class="res-app"> -->
<!-- <ma-svg-icon name="solar:upload-linear" class="res-app-icon" /> -->
<!-- </div> -->
<!-- </div> -->
<!-- <div class="res-app-container"> -->
<!-- <div class="res-app"> -->
<!-- <ma-svg-icon name="solar:upload-linear" class="res-app-icon" /> -->
<!-- </div> -->
<!-- </div> -->
<!-- <div class="res-app-container"> -->
<!-- <div class="res-app"> -->
<!-- <ma-svg-icon name="solar:upload-linear" class="res-app-icon" /> -->
<!-- </div> -->
<!-- </div> -->
<!-- </div> -->
<div class="ma-resource-dock">
<template v-for="btn in resourceStore.getAllButton()">
<div class="res-app-container">
<div class="res-app">
<m-tooltip :text="btn.label">
<input
type="file"
:name="btn.name"
class="hidden"
v-bind="btn?.uploadConfig ?? {}"
@change="handleFile($event, btn)"
@click.stop="() => {}"
>
<ma-svg-icon :name="btn.icon" class="res-app-icon" />
</m-tooltip>
</div>
</div>
</template>
</div>
</div>
</template>

Expand Down Expand Up @@ -625,7 +632,7 @@ onUnmounted(() => {
}

.res-app-icon {
@apply w-55px h-55px !text-2xl transform-all duration-300 text-dark-1 dark-text-gray-2
@apply w-55px h-55px !text-2xl transform-all duration-300 text-dark-1 dark-text-gray-2 cursor-pointer
;
}

Expand Down
90 changes: 90 additions & 0 deletions web/src/store/modules/useResourceStore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/**
* MineAdmin is committed to providing solutions for quickly building web applications
* Please view the LICENSE file that was distributed with this source code,
* For the full copyright and license information.
* Thank you very much for using MineAdmin.
*
* @Author X.Mo<[email protected]>
* @Link https://github.com/mineadmin
*/
import type { Resources } from '#/global'
import { uploadLocal } from '@/utils/uploadLocal.ts'

const resourceDefaultButtons: Resources.Button[] = [
{
name: 'local-image-upload',
label: '图片上传',
icon: 'solar:upload-square-broken',
upload: (files: FileList, args: Resources.Args) => {
const options = { file: files[0] }
uploadLocal(options).then(() => {
args?.getResourceList?.()
}).catch((e) => {
throw new Error(e)
})
},
uploadConfig: {
accept: 'image/*',
limit: 1,
},
order: 0,
},
{
name: 'local-file-upload',
label: '文件上传',
icon: 'hugeicons:file-upload',
upload: (files: FileList, args: Resources.Args) => {
const options = { file: files[0] }
uploadLocal(options).then(() => {
args?.getResourceList?.()
}).catch((e) => {
throw new Error(e)
})
},
uploadConfig: {
accept: '.doc,.xls,.ppt,.txt,.pdf',
limit: 1,
},
order: 1,
},
]

const useResourceStore = defineStore(
'useResourceStore',
() => {
const resourceButtons = ref<Resources.Button[]>([])

const getButton = (name: string): Resources.Button | undefined => {
return resourceButtons.value.find(item => item.name === name)
}

const addButton = (button: Resources.Button): boolean => {
if (getButton(button.name)) {
return false
}
else {
resourceButtons.value.push(button)
return true
}
}

const removeButton = (name: string) => {
resourceButtons.value = resourceButtons.value.filter(item => item.name !== name)
}

const getAllButton = () => {
return resourceButtons.value
}

resourceDefaultButtons.forEach(item => addButton(item))

return {
addButton,
removeButton,
getButton,
getAllButton,
}
},
)

export default useResourceStore
8 changes: 3 additions & 5 deletions web/src/utils/uploadLocal.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import type {UploadRequestOptions} from "element-plus";

/**
* MineAdmin is committed to providing solutions for quickly building web applications
* Please view the LICENSE file that was distributed with this source code,
Expand All @@ -9,14 +7,14 @@ import type {UploadRequestOptions} from "element-plus";
* @Author X.Mo<[email protected]>
* @Link https://github.com/mineadmin
*/
export function uploadLocal(options: UploadRequestOptions, url?: string = '/admin/attachment/upload', key?: string = 'file') {
export function uploadLocal(options: any, url?: string, key?: string) {
const upload = (formData: FormData) => {
return useHttp().post(url, formData)
return useHttp().post(url ?? '/admin/attachment/upload', formData)
}

return new Promise((resolve, reject) => {
const formData = new FormData()
formData.append(key, options.file)
formData.append(key ?? 'file', options.file)
upload(formData).then((res: Record<string, any>) => {
res.code === 200 ? resolve(res) : reject(res)
}).catch((err) => {
Expand Down
1 change: 1 addition & 0 deletions web/types/auto-imports.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ declare global {
const useMenuStore: typeof import('../src/store/modules/useMenuStore')['default']
const useModel: typeof import('vue')['useModel']
const usePluginStore: typeof import('../src/store/modules/usePluginStore')['default']
const useResourceStore: typeof import('../src/store/modules/useResourceStore')['default']
const useRoute: typeof import('vue-router')['useRoute']
const useRouteStore: typeof import('../src/store/modules/useRouteStore')['default']
const useRouter: typeof import('vue-router')['useRouter']
Expand Down
2 changes: 2 additions & 0 deletions web/types/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
Form: typeof import('./../src/components/ma-key-value/components/form.vue')['default']
MaAuth: typeof import('./../src/components/ma-auth/index.vue')['default']
MaCitySelect: typeof import('./../src/components/ma-city-select/index.vue')['default']
MaDialog: typeof import('./../src/components/ma-dialog/index.vue')['default']
Expand All @@ -16,6 +17,7 @@ declare module 'vue' {
MaDrawer: typeof import('./../src/components/ma-drawer/index.vue')['default']
MaIconPanel: typeof import('./../src/components/ma-icon-picker/ma-icon-panel.vue')['default']
MaIconPicker: typeof import('./../src/components/ma-icon-picker/index.vue')['default']
MaKeyValue: typeof import('./../src/components/ma-key-value/index.vue')['default']
MaRemoteSelect: typeof import('./../src/components/ma-remote-select/index.vue')['default']
MaResourcePicker: typeof import('./../src/components/ma-resource-picker/index.vue')['default']
MaSvgIcon: typeof import('./../src/components/ma-svg-icon/index.vue')['default']
Expand Down
23 changes: 23 additions & 0 deletions web/types/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,29 @@ interface ResponseStruct<T> {
data: T
}

declare namespace Resources {

interface Args {
[key: string]: any
}

interface Button {
name: string
label: string
icon: string
click?: (btn: Resources.Button, selected: any[]) => void
upload?: (files: FileList, args: Args) => void
uploadConfig?: Record<string, any>
order?: number
}

interface TypeSetting {
name: string | string[]
icon: string
click?: (...args: any[]) => void
}
}

declare namespace Plugin {

/**
Expand Down

0 comments on commit a8f2b26

Please sign in to comment.