Compare commits

7 Commits
18 changed files with 832 additions and 462 deletions

No files matched your search

+1 -2
View File
@@ -21,8 +21,7 @@
| 长期计划 | | 长期计划 |
|---| |---|
| 重构virtualDirectory,使其不用直接拥有父文件夹 | | 无 |
| 重构virtualDirectory,使其不用直接拥有父文件夹 |
## 碎碎念: ## 碎碎念:
+211 -179
View File
@@ -1,203 +1,235 @@
import config from "@/config"; import config from '@/config'
import generateId from "@/hooks/generateId"; import generateId from '@/hooks/generateId'
import useMiaoFetchApi from "@/hooks/useMiaoFetchApi"; import useMiaoFetchApi from '@/hooks/useMiaoFetchApi'
import type { stats, file, directory } from "@/types/type.ts"; import type { stats, file, directory } from '@/types/type.ts'
import difference from 'lodash/difference'
const miaoFetchApi = useMiaoFetchApi(); const miaoFetchApi = useMiaoFetchApi()
const vItemMap = new Map(); // const vDirectoryMap = new Map<string, VirtualDirectory>()
// const vFileMap = new Map<string, VirtualFile>()
export interface Tree { export interface Tree {
name: string; name: string
type: "directory"; type: 'directory'
path: string; path: string
stats: stats; stats: stats
files?: { files?: {
name: string; name: string
type: "file"; type: 'file'
size: number; size: number
stats: stats; stats: stats
url: string; url: string
}[]; }[]
directories?: Tree[]; directories?: Tree[]
} }
export class VirtualFile { export class VirtualFile {
constructor(info: file, parent: VirtualDirectory) { constructor(info: file, parent: VirtualDirectory) {
this.name = info.name; this.name = info.name
this.size = info.size; this.size = info.size
this.stats = info.stats; this.stats = info.stats
this.parent = parent; this.parent = parent
this.type = "file"; this.type = 'file'
this.id = generateId(); this.id = generateId()
vItemMap.set(this.path, this); // if (vFileMap.get(this.path) !== undefined) {
} // throw `${this.path} is already exist`
name: string; // }
type: "file"; // vFileMap.set(this.path, this)
size: number; }
stats: stats; name: string
parent: VirtualDirectory; type: 'file'
id: number; size: number
storage: { [key: string]: any } = {}; stats: stats
get path() { parent: VirtualDirectory
return `${this.parent.path}${this.name}`; id: number
} storage: { [key: string]: any } = {}
get path() {
return `${this.parent.path}${this.name}`
}
get url() { get url() {
return `${config.api.get}${this.parent.path}${this.name}`; return `${config.api.get}${this.parent.path}${this.name}`
} }
// destroy() {
// vFileMap.delete(this.path)
// }
} }
class VirtualDirectory { class VirtualDirectory {
/** /**
* 除非为根节点,否则必须设置parent * 除非为根节点,否则必须设置parent
*/ */
constructor(info: directory, parent?: VirtualDirectory) { constructor(info: directory, parent?: VirtualDirectory) {
this.name = info.name; this.name = info.name
this.stats = info.stats; this.stats = info.stats
this.parent = parent ?? undefined; this.parent = parent ?? undefined
this.id = generateId(); this.id = generateId()
vItemMap.set(this.path, this); // if (vDirectoryMap.get(this.path) !== undefined) {
} // throw `${this.path} is already exist`
// 文件夹名 // }
name: string; // vDirectoryMap.set(this.path, this)
// 子文件 }
files?: VirtualFile[]; // 文件夹名
// 子目录 name: string
directories?: VirtualDirectory[]; // 子文件
// 没啥意义,标注一下就是了 files: VirtualFile[] = []
type: "directory" = "directory"; // 子目录
// 文件夹创建时间等杂项属性 directories: VirtualDirectory[] = []
stats: stats; // 没啥意义,标注一下就是了
// 父文件夹 type: 'directory' = 'directory'
parent?: VirtualDirectory; // 文件夹创建时间等杂项属性
stats: stats
// 父文件夹
parent?: VirtualDirectory
_isUpdated: boolean = false; _isUpdated: boolean = false
id: number; id: number
// 用来存储一些标记信息啥的 // 用来存储一些标记信息啥的
storage: { [key: string]: any } = {}; storage: { [key: string]: any } = {}
/** /**
* 获取相对于根目录的路径的层数 * 获取相对于根目录的路径的层数
* 用于目录下文件定位 * 用于目录下文件定位
* @returns {string} * @returns {string}
*/ */
get layer(): number { get layer(): number {
return this.parent ? this.parent.layer + 1 : 0; return this.parent ? this.parent.layer + 1 : 0
} }
/** /**
* 相对于根目录的路径, 如: "/", "/123/321/" * 相对于根目录的路径, 如: "/", "/123/321/"
* 用于目录下文件定位 * 用于目录下文件定位
* @returns {string} * @returns {string}
*/ */
get path(): string { get path(): string {
if (!this.parent) { if (!this.parent) {
return "/"; return '/'
} }
return `${this.parent.path}${this.name}/`; return `${this.parent.path}${this.name}/`
} }
/** /**
* 获取从根目录开始的按顺序的文件夹 * 获取从根目录开始的按顺序的文件夹
* @returns {VirtualDirectory[]} * @returns {VirtualDirectory[]}
*/ */
get getParents(): VirtualDirectory[] { get getParents(): VirtualDirectory[] {
if (!this.parent) { if (!this.parent) {
return [this]; return [this]
} }
return [...this.parent.getParents, this]; return [...this.parent.getParents, this]
} }
/** /**
* 获取当前文件夹是否已经请求过至少一次数据 * 获取当前文件夹是否已经请求过至少一次数据
*/ */
get isUpdated() { get isUpdated() {
return this._isUpdated; return this._isUpdated
} }
/** /**
* 获取 layer 级父文件夹, 如getParent(1)就返回父级 * 获取 layer 级父文件夹, 如getParent(1)就返回父级
* @param {number} layer 需要跳转的层数 * @param {number} layer 需要跳转的层数
* @returns {VirtualDirectory} * @returns {VirtualDirectory}
*/ */
getParent(layer: number): VirtualDirectory { getParent(layer: number): VirtualDirectory {
if (layer <= 0 || !this.parent) { if (layer <= 0 || !this.parent) {
return this; return this
} }
return this.parent.getParent(layer - 1); return this.parent.getParent(layer - 1)
} }
get tree(): Tree { get tree(): Tree {
return { return {
name: this.name, name: this.name,
type: this.type, type: this.type,
path: this.path, path: this.path,
stats: this.stats, stats: this.stats,
files: files:
this.files?.map(v => ({ this.files?.map((v) => ({
name: v.name, name: v.name,
type: v.type, type: v.type,
size: v.size, size: v.size,
stats: v.stats, stats: v.stats,
url: v.url url: v.url
})) ?? [], })) ?? [],
directories: this.directories?.map(v => v.tree) ?? [] directories: this.directories?.map((v) => v.tree) ?? []
}; }
} }
// todo: 重构这个函数 // 时间复杂度大爆炸
updateContent(content: (file | directory)[]) { updateContent(content: (file | directory)[]) {
this._isUpdated = true; this._isUpdated = true
this.files = this.files ?? []; const itemNames: string[] = content.map((v) => v.name)
this.directories = this.directories ?? []; // 删去远程端已不存在的文件与文件夹
const fileNames: string[] = this.files.map(v => v.name); const _delete = (target: (VirtualFile | VirtualDirectory)[]) => {
const dirNames: string[] = this.directories.map(v => v.name); let index = 0
const itemNames: string[] = content.map(v => v.name); while (index < target.length) {
// 删去远程端已不存在的文件与文件夹 if (!itemNames.includes(target[index].name)) {
const _delete = (target: (VirtualFile | VirtualDirectory)[]) => { target.splice(index, 1)
let index = 0; } else {
while (index < target.length) { index += 1
if (!itemNames.includes(target[index].name)) { }
target.splice(index, 1); }
} else { }
index += 1; const _addFile = (item: file) => {
} const nvf =
} // vFileMap.get(`${this.path}${item.name}`) ??
}; new VirtualFile(item, this)
_delete(this.files); this.files.push(nvf)
_delete(this.directories); }
// 加入新的文件与文件夹 const _addDirectory = (item: directory) => {
for (let item of content) { const nvd =
if (item.type === "file") { // vDirectoryMap.get(`${this.path}${item.name}/`) ??
if (!fileNames.includes(item.name)) { new VirtualDirectory(item, this)
this.files.push(new VirtualFile(item, this)); this.directories.push(nvd)
} }
} else if (item.type === "directory") { const newFileNames: string[] = difference(
if (!dirNames.includes(item.name)) { content.filter((v) => v.type === 'file').map((v) => v.name),
this.directories.push(new VirtualDirectory(item, this)); this.files.map((v) => v.name)
} )
} const newDirectoryNames: string[] = difference(
} content.filter((v) => v.type === 'directory').map((v) => v.name),
} this.directories.map((v) => v.name)
)
_delete(this.files)
_delete(this.directories)
// 加入新的文件与文件夹
for (let item of content.filter((v) => newFileNames.includes(v.name))) {
_addFile(item as file)
}
for (let item of content.filter((v) =>
newDirectoryNames.includes(v.name)
)) {
_addDirectory(item as directory)
}
}
hasChild(item: VirtualDirectory | VirtualFile): boolean { hasChild(item: VirtualDirectory | VirtualFile): boolean {
if (item.type === "directory") { if (item.type === 'directory') {
return this.directories?.includes(item) ?? false; return this.directories?.includes(item) ?? false
} else { } else {
return this.files?.includes(item) ?? false; return this.files?.includes(item) ?? false
} }
} }
/** // destroy() {
* 调用后重新向远端拉取文件夹信息并进行更新 // for(const item of [...this.files, ...this.directories]){
* @returns {void} // item.destroy()
*/ // }
async update(): Promise<void> { // vDirectoryMap.delete(this.path)
const data: (file | directory)[] = await miaoFetchApi.get(this); // }
this.updateContent(data);
} /**
* 调用后重新向远端拉取文件夹信息并进行更新
* @returns {void}
*/
async update(): Promise<void> {
const data: (file | directory)[] = await miaoFetchApi.get(this)
this.updateContent(data)
}
} }
export default VirtualDirectory; export default VirtualDirectory
+1
View File
@@ -89,6 +89,7 @@ defineExpose({ alertTip });
width: 100%; width: 100%;
height: 100%; height: 100%;
position: relative; position: relative;
overflow: hidden;
.miao-message-provider-container { .miao-message-provider-container {
position: absolute; position: absolute;
+14 -6
View File
@@ -1,5 +1,9 @@
<template> <template>
<div class="container" @dragover="handleDragOver" @dragleave="handleDragLeave" @drop="handleDrop"> <div
class="container"
@dragover="handleDragOver"
@dragleave="handleDragLeave"
@drop="handleDrop">
<slot></slot> <slot></slot>
<miao-mask v-model:show="showMask"></miao-mask> <miao-mask v-model:show="showMask"></miao-mask>
</div> </div>
@@ -64,11 +68,15 @@ const removeDirectory = async (files: File[]) => {
} }
} }
} }
const check = (event: DragEvent) => [ const check = (event: DragEvent) => {
...(event.dataTransfer?.files ?? []), return (
...(dataBus.get('dragData_vFiles') ?? []), [
...(dataBus.get('dragData_vDirectory') ?? []) ...(event.dataTransfer?.types?.filter(v=>v === 'Files') ?? []),
].length !== 0 ...(dataBus.get('dragData_vFiles') ?? []),
...(dataBus.get('dragData_vDirectory') ?? [])
].length !== 0
)
}
const handleDragOver = async (event: DragEvent) => { const handleDragOver = async (event: DragEvent) => {
if (props.disable || check(event) === false) { if (props.disable || check(event) === false) {
+6 -1
View File
@@ -53,7 +53,12 @@ export const renderIcon = (icon: any) => {
} }
} }
export const sleep = async (timeout: number) => {
await new Promise(_=>setTimeout(_, timeout))
}
export default { export default {
filePicker, filePicker,
renderIcon renderIcon,
sleep
} }
+84 -47
View File
@@ -1,28 +1,49 @@
<template> <template>
<miao-drop-handler @on-virtual-directory="handleDropVDirectory" @on-files="handleDropFiles" <miao-drop-handler
@on-virtual-directory="handleDropVDirectory"
@on-files="handleDropFiles"
@on-virtual-files="handleDropVirtualFiles"> @on-virtual-files="handleDropVirtualFiles">
<div class="miao-directory-item-container" ref="rootDomRef"> <miaoAlertTipProvider ref="miaoAlertTip">
<!-- 顶部面包屑导航和工具栏 --> <div class="miao-directory-item-container" ref="rootDomRef">
<miao-directory-top :color="props.color" :current-directory="currentDirectory" @back="handleBack" <!-- 顶部面包屑导航和工具栏 -->
:open-menu-option="topBarMenuOption" @reload="handleReload" @exit="emit('exit')" :index="index" <miao-directory-top
@set-current-directory="setCurrentDirectory" @search="handleSearch" :color="props.color"
@menu-select="handleTopBarMenuSelect" /> :current-directory="currentDirectory"
@back="handleBack"
:open-menu-option="topBarMenuOption"
@reload="handleReload"
@exit="emit('exit')"
:index="index"
@set-current-directory="setCurrentDirectory"
@search="handleSearch"
@menu-select="handleTopBarMenuSelect" />
<!-- 文件和文件夹列表 --> <!-- 文件和文件夹列表 -->
<miao-directory-items ref="miaoDirectoryItemRef" :color="props.color" <miao-directory-items
:showData_directory="showData_directory" :showData_files="showData_files" ref="miaoDirectoryItemRef"
:selected-item="selectedItem.value" :index="index" @item-click="handleItemClick" :color="props.color"
@item-download="handleItemDownload" @item-delete="handleItemDelete" :showData_directory="showData_directory"
@item-drag-start="handleItemDragStart" @item-rename="handleItemRename" :showData_files="showData_files"
@item-select="handleItemSelect" /> :selected-item="selectedItem.value"
:index="index"
@item-click="handleItemClick"
@item-download="handleItemDownload"
@item-delete="handleItemDelete"
@item-drag-start="handleItemDragStart"
@item-rename="handleItemRename"
@item-select="handleItemSelect" />
<!-- 底部工具栏 --> <!-- 底部工具栏 -->
<miao-directory-bottom :index="index" @add-new-item="handleAddNewItem" v-if="false" <miao-directory-bottom
@pick-files-upload="handlePickFilesUpload" /> :index="index"
@add-new-item="handleAddNewItem"
v-if="false"
@pick-files-upload="handlePickFilesUpload" />
<miao-mask v-model:show="showModel"></miao-mask> <miao-mask v-model:show="showModel"></miao-mask>
<miao-popup-input ref="popupInput"></miao-popup-input> <miao-popup-input ref="popupInput"></miao-popup-input>
</div> </div>
</miaoAlertTipProvider>
</miao-drop-handler> </miao-drop-handler>
</template> </template>
@@ -37,13 +58,13 @@ import useVirtualPages from '@/hooks/useVirtualPages'
import { filePicker, renderIcon } from '@/hooks/miaoTools' import { filePicker, renderIcon } from '@/hooks/miaoTools'
import uniq from 'lodash/uniq' import uniq from 'lodash/uniq'
// 导入子组件
import miaoDirectoryTop from './components/miaoDirectoryTop.vue' import miaoDirectoryTop from './components/miaoDirectoryTop.vue'
import MiaoDirectoryItems from './components/miaoDirectoryItemsContainer.vue' import MiaoDirectoryItems from './components/miaoDirectoryItemsContainer.vue'
import MiaoDirectoryBottom from './components/miaoDirectoryBottom.vue' import MiaoDirectoryBottom from './components/miaoDirectoryBottom.vue'
import MiaoMask from '@/components/miaoMask.vue' import MiaoMask from '@/components/miaoMask.vue'
import MiaoPopupInput from '@/components/miaoPopupInput.vue' import MiaoPopupInput from '@/components/miaoPopupInput.vue'
import MiaoDropHandler from '@/components/miaoDropHandler.vue' import MiaoDropHandler from '@/components/miaoDropHandler.vue'
import miaoAlertTipProvider from '@/components/miaoAlertTipProvider.vue'
import { import {
CloseOutline, CloseOutline,
SearchOutline, SearchOutline,
@@ -80,6 +101,7 @@ const searchText = ref<string>('')
const showDirs = ref<boolean>(true) const showDirs = ref<boolean>(true)
const showFiles = ref<boolean>(true) const showFiles = ref<boolean>(true)
const miaoDirectoryItemRef = ref() const miaoDirectoryItemRef = ref()
const miaoAlertTip = ref<InstanceType<typeof miaoAlertTipProvider>>()
class SelectedItem { class SelectedItem {
constructor() { constructor() {
@@ -182,10 +204,10 @@ const topBarMenuOption = computed(() => {
}, },
searchText.value !== '' searchText.value !== ''
? { ? {
label: '重置搜索', label: '重置搜索',
key: 'searchReset', key: 'searchReset',
icon: renderIcon(SearchOutline) icon: renderIcon(SearchOutline)
} }
: undefined, : undefined,
// selectedItem.value.length !== 0 // selectedItem.value.length !== 0
// ? { // ? {
@@ -227,13 +249,14 @@ const topBarMenuOption = computed(() => {
label: `反选`, label: `反选`,
key: 'multiSelect:inversion' key: 'multiSelect:inversion'
}, },
selectedItem.value.length === 2 ? selectedItem.value.length === 2
{ ? {
label: `区间内全选`, label: `区间内全选`,
key: 'multiSelect:range' key: 'multiSelect:range'
} : undefined }
: undefined
].filter((v) => v !== undefined), ].filter((v) => v !== undefined),
icon: renderIcon(CheckmarkDoneOutline), icon: renderIcon(CheckmarkDoneOutline)
}, },
{ {
label: '插件', label: '插件',
@@ -259,11 +282,9 @@ const topBarMenuOption = computed(() => {
// 设置显示的dir // 设置显示的dir
const setCurrentDirectory = async (virtualDirectory: VirtualDirectory) => { const setCurrentDirectory = async (virtualDirectory: VirtualDirectory) => {
try {
// 若没有缓存数据则同步刷新,有缓存数据则异步刷新 // 若没有缓存数据则同步刷新,有缓存数据则异步刷新
if ( if (!virtualDirectory.isUpdated) {
virtualDirectory.files === undefined ||
virtualDirectory.directories === undefined
) {
await virtualDirectory.update() await virtualDirectory.update()
} else { } else {
virtualDirectory.update() virtualDirectory.update()
@@ -279,6 +300,12 @@ const setCurrentDirectory = async (virtualDirectory: VirtualDirectory) => {
}) })
} }
currentDirectories.value[0] = virtualDirectory currentDirectories.value[0] = virtualDirectory
} catch(e) {
miaoAlertTip.value?.alertTip(`获取文件夹内容出错`, {
type: 'error',
timeout: 3000
})
}
} }
const reload = () => { const reload = () => {
@@ -315,10 +342,12 @@ const handleTopBarMenuSelect = (key: string) => {
// selectedItem.clear() // selectedItem.clear()
} else if (key === 'showDirs') { } else if (key === 'showDirs') {
showDirs.value = !showDirs.value showDirs.value = !showDirs.value
showDirs.value === false && selectedItem.clear(v => v.type !== 'directory') showDirs.value === false &&
selectedItem.clear((v) => v.type !== 'directory')
} else if (key === 'showFiles') { } else if (key === 'showFiles') {
showFiles.value = !showFiles.value showFiles.value = !showFiles.value
showFiles.value === false && selectedItem.clear(v => v.type !== 'file') showFiles.value === false &&
selectedItem.clear((v) => v.type !== 'file')
} else if (key.startsWith('plugin:')) { } else if (key.startsWith('plugin:')) {
const pluginName = key.split(':')[1] const pluginName = key.split(':')[1]
pluginCenter.usePlugin(pluginName, [currentDirectory.value], []) pluginCenter.usePlugin(pluginName, [currentDirectory.value], [])
@@ -326,20 +355,29 @@ const handleTopBarMenuSelect = (key: string) => {
const opName = key.split(':')[1] const opName = key.split(':')[1]
switch (opName) { switch (opName) {
case 'all': case 'all':
selectedItem.pushArray([...showData_directory.value, ...showData_files.value]) selectedItem.pushArray([
break; ...showData_directory.value,
...showData_files.value
])
break
case 'reset': case 'reset':
selectedItem.clear() selectedItem.clear()
break break
case 'inversion': case 'inversion':
const sArr = selectedItem.value const sArr = selectedItem.value
const arr = [...showData_directory.value, ...showData_files.value].filter(i => !sArr.includes(i)) const arr = [
...showData_directory.value,
...showData_files.value
].filter((i) => !sArr.includes(i))
selectedItem.clear() selectedItem.clear()
selectedItem.pushArray(arr) selectedItem.pushArray(arr)
break break
case "range": case 'range':
const _sArr = selectedItem.value const _sArr = selectedItem.value
const _arr = [...showData_directory.value, ...showData_files.value] const _arr = [
...showData_directory.value,
...showData_files.value
]
const _nArr = [] const _nArr = []
let _counter = 0 let _counter = 0
for (const i of _arr) { for (const i of _arr) {
@@ -357,7 +395,6 @@ const handleTopBarMenuSelect = (key: string) => {
selectedItem.pushArray(_nArr) selectedItem.pushArray(_nArr)
break break
} }
} }
} }
@@ -377,7 +414,7 @@ const handleItemClick = (item: VirtualDirectory | VirtualFile) => {
} else { } else {
if (item.type === 'file') { if (item.type === 'file') {
const useablePlugins = pluginCenter.getUsablePlugin([], [item]) const useablePlugins = pluginCenter.getUsablePlugin([], [item])
if(useablePlugins.length > 0){ if (useablePlugins.length > 0) {
pluginCenter.usePlugin(useablePlugins[0].key, [], [item]) pluginCenter.usePlugin(useablePlugins[0].key, [], [item])
} else { } else {
openUrl(`${baseUrl}${api.get}${item.path}`) openUrl(`${baseUrl}${api.get}${item.path}`)
@@ -396,7 +433,7 @@ const handleItemDownload = (item: VirtualFile) => {
const handleItemDelete = async (_item: VirtualDirectory | VirtualFile) => { const handleItemDelete = async (_item: VirtualDirectory | VirtualFile) => {
for (let item of uniq([...selectedItem.value, _item])) { for (let item of uniq([...selectedItem.value, _item])) {
; (async () => { ;(async () => {
const { response } = miaoFetchApi.delete(item, { const { response } = miaoFetchApi.delete(item, {
retry: 5 retry: 5
}) })
@@ -485,7 +522,7 @@ const handleDropVDirectory = async (vDirs: VirtualDirectory[]) => {
} }
} }
for (let dir of vDirs) { for (let dir of vDirs) {
; (async () => { ;(async () => {
const _from = dir.parent const _from = dir.parent
const { response } = miaoFetchApi.cut(dir, currentDirectory.value) const { response } = miaoFetchApi.cut(dir, currentDirectory.value)
const id = (await response).eventId const id = (await response).eventId
@@ -504,7 +541,7 @@ const handleDropVirtualFiles = (files: VirtualFile[]) => {
return return
} }
for (let file of files) { for (let file of files) {
; (async () => { ;(async () => {
const _from = file.parent const _from = file.parent
const { response } = miaoFetchApi.cut(file, currentDirectory.value) const { response } = miaoFetchApi.cut(file, currentDirectory.value)
const id = (await response).eventId const id = (await response).eventId
+2 -2
View File
@@ -17,13 +17,13 @@ const pluginConfig: registerComponentOption = {
return false return false
} }
for (let i of vFiles) { for (let i of vFiles) {
for (let s of ['mp3', 'flac', 'ogg']) { for (let s of ['mp3', 'flac', 'ogg', 'mp4']) {
if (i.name.endsWith(s)) { if (i.name.endsWith(s)) {
return true return true
} }
} }
} }
return vFiles[0].name.endsWith('?') return false
}, },
disable: false, disable: false,
single: true, single: true,
@@ -1,10 +1,11 @@
import { createApp, Reactive } from "vue"; import { createApp, Reactive } from "vue";
import aplayerListNew from "../src/aplayerListNew.vue"; import aplayerListNew from "../src/aplayerListNew.vue";
import apType from "../types/ap"; import apType, { audioType } from "../types/ap";
type aplayerMiaoOption = { type aplayerMiaoOption = {
onDragItemStart?: () => void, onDragItemStart?: () => void
onDragItemEnd?: () => void, onDragItemEnd?: () => void
onPlayVideo?: (element: audioType) => void
} }
/** /**
@@ -38,6 +39,8 @@ const useAplayerMiao = (ap: Reactive<apType>, options?: aplayerMiaoOption) => {
const app = createApp(aplayerListNew, { ap, ...(options ?? {}) }); const app = createApp(aplayerListNew, { ap, ...(options ?? {}) });
app.mount(new_list_dom); app.mount(new_list_dom);
// console.log({ap})
ap.on("destroy", () => { ap.on("destroy", () => {
app.unmount(); app.unmount();
}); });
@@ -1,118 +1,157 @@
<template> <template>
<miao-drop-handler @on-virtual-files="handleDropVFiles"> <miao-drop-handler @on-virtual-files="handleDropVFiles">
<miao-alertTip-provider ref="miaoMessageRef"> <miao-alert-tip-provider ref="miaoMessageRef">
<div class="miaoMusic-container"> <div class="miaoMusic-container">
<NScrollbar> <NScrollbar>
<div class="miaoMusic-main"> <miao-music-main v-if="ap" :ap="ap"></miao-music-main>
</div>
</NScrollbar> </NScrollbar>
<div ref="aplayer" /> <miao-mask
:show="isAPlayerListShow"
style="z-index: 5"
@click="() => ap?.list.hide()"></miao-mask>
<div ref="aplayer" style="z-index: 10" />
</div> </div>
</miao-alertTip-provider> </miao-alert-tip-provider>
</miao-drop-handler> </miao-drop-handler>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import useAplayerMiao from './hooks/aplayerMiao'
import { VirtualFile } from '@/class/VirtualDirectory'
import { App, onMounted, onUnmounted, reactive, ref, watch } from 'vue'
import { NScrollbar } from 'naive-ui'
import './src/APlayer.fix.css'
// @ts-ignore
import APlayer from 'APlayer'
import config from '@/config'
import difference from 'lodash/difference'
import uniq from 'lodash/uniq'
import apType from './types/ap'
import miaoDropHandler from '@/components/miaoDropHandler.vue' import miaoDropHandler from '@/components/miaoDropHandler.vue'
import miaoAlertTipProvider from '@/components/miaoAlertTipProvider.vue' import miaoAlertTipProvider from '@/components/miaoAlertTipProvider.vue'
import useAplayerMiao from './hooks/aplayerMiao'; import usePluginCenter from '@/hooks/usePluginCenter'
import { VirtualFile } from '@/class/VirtualDirectory'; import VirtualPage from '@/class/VirtualPage'
import { onMounted, onUnmounted, reactive, ref, watch } from 'vue'; import miaoMask from '@/components/miaoMask.vue'
import { NScrollbar } from 'naive-ui'; import miaoMusicMain from './src/main.vue'
import "./src/APlayer.fix.css"
// @ts-ignore const pluginCenter = usePluginCenter()
import APlayer from 'APlayer'; let ap = ref<apType>()
import config from '@/config'; let vueApp: App<Element>
import difference from 'lodash/difference';
import uniq from 'lodash/uniq'
import apType from './types/ap';
const miaoMessageRef = ref<InstanceType<typeof miaoAlertTipProvider>>() const miaoMessageRef = ref<InstanceType<typeof miaoAlertTipProvider>>()
const aplayer = ref<any>(null)
const isAPlayerListShow = ref(true)
const currentFiles = defineModel<VirtualFile[]>('currentFiles', { const currentFiles = defineModel<VirtualFile[]>('currentFiles', {
required: true required: true
});
watch(() => currentFiles.value, (value, oldValue) => {
const diff = difference(value, oldValue)
if (diff.length > 0) {
const _s = `等${diff.length}个资源`
miaoMessageRef.value?.alertTip(`已添加 ${diff[0].name}${diff.length !== 1 ? _s : ''}`, {
type: 'success',
timeout: 3000
})
ap.list.add(getMusicList(diff))
}
}) })
const getMusicList = (mp3VFiles: VirtualFile[]) => mp3VFiles.map((v, index) => ({ const props = defineProps<{
url: `${config.baseUrl}${v.url}`, view: VirtualPage
name: v.name, }>()
artist: ' ',
index watch(
})); () => currentFiles.value,
(value, oldValue) => {
const diff = difference(value, oldValue)
if (diff.length > 0) {
const _s = `等${diff.length}个资源`
miaoMessageRef.value?.alertTip(
`已添加 ${diff[0].name}${diff.length !== 1 ? _s : ''}`,
{
type: 'success',
timeout: 3000
}
)
ap?.value?.list.add(getMusicList(diff))
}
}
)
const getMusicList = (mp3VFiles: VirtualFile[]) =>
mp3VFiles.map((v, index) => ({
url: `${config.baseUrl}${v.url}`,
name: v.name,
artist: ' ',
index
}))
const handleDropVFiles = (files: VirtualFile[]) => { const handleDropVFiles = (files: VirtualFile[]) => {
currentFiles.value = uniq([...currentFiles.value, ...files]); currentFiles.value = uniq([...currentFiles.value, ...files])
}; }
const aplayer = ref<any>(null); const initAPlayerMiao = () => {
const disableDropReceiver = ref(false)
let ap: apType
let vueApp: any
onMounted(() => {
currentFiles.value = [...currentFiles.value.reverse()]
const aplayerOption = { const aplayerOption = {
container: aplayer.value, container: aplayer.value,
audio: getMusicList(currentFiles.value), audio: getMusicList(currentFiles.value),
autoplay: true, autoplay: true,
volume: 1, volume: 1,
theme: "#60e8a2" theme: '#60e8a2',
listMaxHeight: '500px'
} }
ap = reactive<apType>(new APlayer(aplayerOption)) ap.value = reactive<apType>(new APlayer(aplayerOption))
vueApp = useAplayerMiao(ap, { vueApp = useAplayerMiao(ap.value, {
onDragItemStart: () => { onDragItemStart: () => {},
disableDropReceiver.value = true onDragItemEnd: () => {},
}, onPlayVideo: (element) => {
onDragItemEnd: () => { pluginCenter.usePlugin(
disableDropReceiver.value = false 'miaoVideoPlayer',
}, [],
[
currentFiles.value.find(
(v) => v.name === element.name
) as VirtualFile
]
)
}
}) })
ap.on('listremove', (...args: any) => { ap.value.on('listremove', (...args: any) => {
const { index } = args[0] as { index: number } const { index } = args[0] as { index: number }
const _name = ap.list.audios[index].name const _name = ap?.value?.list.audios[index].name
currentFiles.value = currentFiles.value.filter(v => v.name !== _name) currentFiles.value = currentFiles.value.filter((v) => v.name !== _name)
}) })
vueApp ap.value.on('play', () => {
}); const name = ap.value?.list.audios[ap.value.list.index].name as string
props.view.setTitle(name)
})
ap.value.on('listshow', () => {
isAPlayerListShow.value = true
})
ap.value.on('listhide', () => {
isAPlayerListShow.value = false
})
}
onMounted(() => {
currentFiles.value = [...currentFiles.value.reverse()]
initAPlayerMiao()
})
onUnmounted(() => { onUnmounted(() => {
ap.destroy() vueApp.unmount()
ap?.value?.destroy()
}) })
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.miaoMusic-container { .miaoMusic-container {
position: relative;
width: 100%; width: 100%;
height: 100%; height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
.miaoMusic-main {
padding-bottom: 88px;
}
} }
</style> </style>
<style lang="scss"> <style lang="scss">
.aplayer { .aplayer {
display: flex; display: flex;
flex-direction: column-reverse; flex-direction: column-reverse;
position: fixed; position: absolute;
bottom: 10px; bottom: 10px;
width: -webkit-fill-available; width: -webkit-fill-available;
.aplayer-icon-order,
.aplayer-icon-loop {
display: none !important;
}
} }
</style> </style>
@@ -3,7 +3,7 @@
font-family: Arial, Helvetica, sans-serif; font-family: Arial, Helvetica, sans-serif;
margin: 5px; margin: 5px;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .07), 0 1px 5px 0 rgba(0, 0, 0, .1); box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .07), 0 1px 5px 0 rgba(0, 0, 0, .1);
border-radius: 2px; border-radius: 5px;
overflow: hidden; overflow: hidden;
-webkit-user-select: none; -webkit-user-select: none;
-moz-user-select: none; -moz-user-select: none;
@@ -22,10 +22,10 @@
height: 100% height: 100%
} }
.aplayer svg circle, /* .aplayer svg circle,
.aplayer svg path { .aplayer svg path {
fill: #fff fill: #fff
} } */
.aplayer.aplayer-withlist .aplayer-info { .aplayer.aplayer-withlist .aplayer-info {
border-bottom: 1px solid #e9e9e9 border-bottom: 1px solid #e9e9e9
@@ -427,11 +427,11 @@
fill: #000 fill: #000
} }
.aplayer .aplayer-info .aplayer-controller .aplayer-time .aplayer-icon.aplayer-icon-menu, /* .aplayer .aplayer-info .aplayer-controller .aplayer-time .aplayer-icon.aplayer-icon-menu,
.aplayer .aplayer-info .aplayer-controller .aplayer-time.aplayer-time-narrow .aplayer-icon-menu, .aplayer .aplayer-info .aplayer-controller .aplayer-time.aplayer-time-narrow .aplayer-icon-menu,
.aplayer .aplayer-info .aplayer-controller .aplayer-time.aplayer-time-narrow .aplayer-icon-mode { .aplayer .aplayer-info .aplayer-controller .aplayer-time.aplayer-time-narrow .aplayer-icon-mode {
display: none display: none
} } */
.aplayer .aplayer-info .aplayer-controller .aplayer-volume-wrap { .aplayer .aplayer-info .aplayer-controller .aplayer-volume-wrap {
position: relative; position: relative;
@@ -556,9 +556,9 @@
.aplayer .aplayer-list { .aplayer .aplayer-list {
overflow: auto; overflow: auto;
transition: all .5s ease; transition: all .35s ease;
will-change: height; will-change: height;
display: none; /* display: none; */
overflow: hidden overflow: hidden
} }
@@ -604,9 +604,9 @@
border-top: none border-top: none
} }
.aplayer .aplayer-list ol li:hover { /* .aplayer .aplayer-list ol li:hover {
background: #efefef background: #efefef
} } */
.aplayer .aplayer-list ol li.aplayer-list-light { .aplayer .aplayer-list ol li.aplayer-list-light {
background: #e9e9e9 background: #e9e9e9
@@ -1,70 +1,164 @@
<template> <template>
<ol :style="{ maxHeight: ap.options.listMaxHeight }" :key="refreshKey" ref="ol"> <div class="aplayer-list-top">
<VueDraggable v-model="ap.list.audios" @start="onDragStart" @end="onDragEnd" item-key="id" <div @click="loopModeToggle" class="aplayer-list-top-loopMode">
handle=".aplayer-list-control-btn-reorder" ghostClass="iSDragging" :animation="150"> <component
<miaoLazyDiv v-for="(element, index) in ap.list.audios" :min-height="'32px'" auto-height> :is="loopModeMap[loopMode].icon"
<li :class="activeAudioIndex == index ? 'aplayer-list-light' : ''" class="aplayer-list-new" class="aplayer-list-top-loopMode-icon"></component>
:key="element.id"> <span class="aplayer-list-top-loopMode-name">
<span class="aplayer-list-cur" :style="{ backgroundColor: apTheme }"></span> {{ loopModeMap[loopMode].name }}
<span class="aplayer-list-index">{{ index + 1 }}</span> </span>
<span class="aplayer-list-title">{{ element.name }}</span> </div>
<div class="aplayer-list-control"> <div class="aplayer-list-top-buttons">
<div class="aplayer-list-control-btn aplayer-list-control-btn-hover aplayer-list-control-btn-play" <component :is="AddCircleOutline" class="aplayer-list-top-buttons-icon"></component>
@click.stop="switchAudio(index)"> </div>
<PlayOutline class="aplayer-list-control-btn-svg" /> <span></span>
</div> </div>
<div class="aplayer-list-control-btn aplayer-list-control-btn-hover" <ol
@click.stop="switchAudio(index)" v-if="false"> :style="{ maxHeight: ap.options.listMaxHeight }"
<HeartOutline class="aplayer-list-control-btn-svg" /> :class="[
</div> !isDragging ? 'miaoMusic-ol-isDragging' : 'miaoMusic-ol-notDragging'
<div class="aplayer-list-control-btn" @click.stop="listAudioUp(index)" v-if="false"> ]"
<ChevronUpOutline class="aplayer-list-control-btn-svg" /> :key="refreshKey"
</div> ref="ol">
<div class="aplayer-list-control-btn" @click.stop="listAudioDown(index)" v-if="false"> <VueDraggable
<ChevronDownOutline class="aplayer-list-control-btn-svg" /> v-model="ap.list.audios"
</div> @start="onDragStart"
<div class="aplayer-list-control-btn aplayer-list-control-btn-hover" @end="onDragEnd"
@click.stop="listAudioRemove(index)"> item-key="id"
<CloseOutline class="aplayer-list-control-btn-svg" /> handle=".aplayer-list-control-btn-reorder"
</div> ghostClass="iSDragging"
<div class="aplayer-list-control-btn aplayer-list-control-btn-reorder"> :animation="150">
<ReorderFourOutline class="aplayer-list-control-btn-svg" /> <!-- <miaoLazyDiv :min-height="'32px'" auto-height> -->
</div> <li
v-for="(element, index) in ap.list.audios"
:class="[activeAudioIndex == index ? 'aplayer-list-light' : '']"
class="aplayer-list-new"
:key="element.id">
<span
class="aplayer-list-cur"
:style="{ backgroundColor: apTheme }"></span>
<span class="aplayer-list-index">{{ index + 1 }}</span>
<span class="aplayer-list-title">{{ element.name }}</span>
<div class="aplayer-list-control">
<div
v-if="isVideo(element)"
class="aplayer-list-control-btn aplayer-list-control-btn-hover aplayer-list-control-btn-play"
@click.stop="onPlayVideo(element)">
<VideocamOutline class="aplayer-list-control-btn-svg" />
</div> </div>
<span class="aplayer-list-author" v-show="element.artist !== 'Audio artist1'">{{ element.artist <div
}}</span> class="aplayer-list-control-btn aplayer-list-control-btn-hover aplayer-list-control-btn-play"
</li> @click.stop="switchAudio(index)">
</miaoLazyDiv> <component
:is="
activeAudioIndex == index && !isPaused
? PauseOutline
: PlayOutline
"
class="aplayer-list-control-btn-svg"></component>
</div>
<div
class="aplayer-list-control-btn aplayer-list-control-btn-hover"
@click.stop="switchAudio(index)"
v-if="false">
<HeartOutline class="aplayer-list-control-btn-svg" />
</div>
<div
class="aplayer-list-control-btn"
@click.stop="listAudioUp(index)"
v-if="false">
<ChevronUpOutline
class="aplayer-list-control-btn-svg" />
</div>
<div
class="aplayer-list-control-btn"
@click.stop="listAudioDown(index)"
v-if="false">
<ChevronDownOutline
class="aplayer-list-control-btn-svg" />
</div>
<div
class="aplayer-list-control-btn aplayer-list-control-btn-hover"
@click.stop="listAudioRemove(index)">
<CloseOutline class="aplayer-list-control-btn-svg" />
</div>
<div
class="aplayer-list-control-btn aplayer-list-control-btn-reorder">
<ReorderFourOutline
class="aplayer-list-control-btn-svg" />
</div>
</div>
<span
class="aplayer-list-author"
v-show="element.artist !== 'Audio artist1'">
{{ element.artist }}
</span>
</li>
<!-- </miaoLazyDiv> -->
</VueDraggable> </VueDraggable>
</ol> </ol>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, nextTick, onMounted, ref } from 'vue'; import { computed, nextTick, onMounted, ref } from 'vue'
import { ChevronUpOutline, ChevronDownOutline, CloseOutline, ReorderFourOutline, PlayOutline, HeartOutline } from '@vicons/ionicons5'; import {
ChevronUpOutline,
ChevronDownOutline,
CloseOutline,
ReorderFourOutline,
PlayOutline,
HeartOutline,
VideocamOutline,
PauseOutline,
ListCircleOutline,
ListOutline,
HeadsetOutline,
LogoOctocat,
AddCircleOutline
} from '@vicons/ionicons5'
import { VueDraggable } from 'vue-draggable-plus' import { VueDraggable } from 'vue-draggable-plus'
import miaoLazyDiv from '@/components/miaoLazyDiv.vue'; // 与vue-draggable冲突
import apType, { audioType } from '../types/ap'; // import miaoLazyDiv from '@/components/miaoLazyDiv.vue';
import apType, { audioType } from '../types/ap'
const props = defineProps<{ const props = defineProps<{
ap: apType ap: apType
onDragItemStart: () => void, onDragItemStart: () => void
onDragItemEnd: () => void onDragItemEnd: () => void
onPlayVideo: (element: audioType) => void
}>() }>()
const apRef = ref(props.ap)
const ol = ref<HTMLOListElement>() const ol = ref<HTMLOListElement>()
const refreshKey = ref(Math.random()) const refreshKey = ref(Math.random())
const isDragging = ref(false) const isDragging = ref(false)
const apTheme = computed(() => props.ap.list.audios[activeAudioIndex.value].theme ?? props.ap.options.theme) const apTheme = computed(
() =>
props.ap.list.audios[activeAudioIndex.value].theme ??
props.ap.options.theme
)
const activeAudioIndex = computed(() => props.ap.list.index) const activeAudioIndex = computed(() => props.ap.list.index)
const isPaused = ref(false)
props.ap.on('play', () => {
nextTick(() => {
isPaused.value = false
})
})
props.ap.on('pause', () => {
isPaused.value = true
})
props.ap.on('listswitch', (...args: any) => { props.ap.on('listswitch', (...args: any) => {
// 手动触发更新,不然监听不到 // 手动触发更新,不然监听不到
props.ap.list.index = args[0].index as number; props.ap.list.index = args[0].index as number
}) })
const refresh = () => { const refresh = () => {
@@ -72,7 +166,7 @@ const refresh = () => {
refreshKey.value = Math.random() refreshKey.value = Math.random()
nextTick(() => { nextTick(() => {
ol.value?.scrollTo({ ol.value?.scrollTo({
top, top
}) })
}) })
} }
@@ -82,6 +176,12 @@ props.ap.on('listadd', refresh)
const switchAudio = (index: number) => { const switchAudio = (index: number) => {
if (index === activeAudioIndex.value) { if (index === activeAudioIndex.value) {
if (props.ap.paused) {
props.ap.play()
} else {
props.ap.pause()
}
props.ap.paused = props.ap.paused
return return
} }
props.ap.list.switch(index) props.ap.list.switch(index)
@@ -96,33 +196,33 @@ const listRefreshIndex = (curr: audioType) => {
} }
const listAudioUp = (index: number) => { const listAudioUp = (index: number) => {
const arr = props.ap.list.audios; const arr = props.ap.list.audios
const len = arr.length const len = arr.length
const curr = props.ap.list.audios[props.ap.list.index] const curr = props.ap.list.audios[props.ap.list.index]
if (index === 0) { if (index === 0) {
const _item = arr.shift(); const _item = arr.shift()
_item && arr.push(_item); _item && arr.push(_item)
} else { } else {
const pre: number = (len - 1 + index) % len; const pre: number = (len - 1 + index) % len
const _pre = arr[index]; const _pre = arr[index]
arr[index] = arr[pre]; arr[index] = arr[pre]
arr[pre] = _pre; arr[pre] = _pre
} }
listRefreshIndex(curr) listRefreshIndex(curr)
}; }
const listAudioDown = (index: number) => { const listAudioDown = (index: number) => {
const arr = props.ap.list.audios; const arr = props.ap.list.audios
const len = arr.length const len = arr.length
const curr = props.ap.list.audios[props.ap.list.index] const curr = props.ap.list.audios[props.ap.list.index]
if (index === len - 1) { if (index === len - 1) {
const _item = arr.pop(); const _item = arr.pop()
_item && arr.unshift(_item); _item && arr.unshift(_item)
} else { } else {
const pre: number = (len + 1 + index) % len; const pre: number = (len + 1 + index) % len
const _pre = arr[index]; const _pre = arr[index]
arr[index] = arr[pre]; arr[index] = arr[pre]
arr[pre] = _pre; arr[pre] = _pre
} }
listRefreshIndex(curr) listRefreshIndex(curr)
} }
@@ -144,13 +244,61 @@ const { onDragStart, onDragEnd } = (() => {
} }
} }
})() })()
// const onDragEnd = (_e: any) => {
// const curr = props.ap.list.audios[props.ap.list.index] const isVideo = (element: audioType) => {
// listRefreshIndex(curr) for (let end of ['mp4']) {
// } if (element.name.endsWith(end)) {
return true
}
}
return false
}
const loopModeMap = {
0: { name: '列表循环', icon: ListCircleOutline },
1: { name: '单曲循环', icon: HeadsetOutline },
2: { name: '列表播放', icon: ListOutline },
3: { name: '随机播放', icon: LogoOctocat }
}
const loopMode = computed(() => {
if (apRef.value.options.order === 'list') {
switch (apRef.value.options.loop) {
case 'all':
return 0
case 'one':
return 1
case 'none':
return 2
}
}
return 3
})
const loopModeToggle = () => {
const ops = apRef.value.options
if (ops.order === 'random') {
ops.order = 'list'
} else {
switch (ops.loop) {
case 'all': {
ops.loop = 'one'
break
}
case 'one': {
ops.loop = 'none'
break
}
case 'none': {
ops.loop = 'all'
ops.order = 'random'
break
}
}
}
}
onMounted(() => { onMounted(() => {
// console.log(props.ap) console.log(props.ap)
}) })
</script> </script>
@@ -188,7 +336,7 @@ onMounted(() => {
&-svg { &-svg {
width: 20px; width: 20px;
aspect-ratio: 1; aspect-ratio: 1;
transition: all .1s ease; transition: all 0.1s ease;
} }
} }
@@ -200,6 +348,60 @@ onMounted(() => {
} }
} }
} }
.aplayer-list-top {
height: 30px;
// width: auto;
// max-width: 100%;
position: relative;
border-bottom: 1px solid #e9e9e9;
display: flex;
justify-items: baseline;
align-items: center;
padding: 0 10px;
.aplayer-list-top-loopMode {
text-align: center;
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
float: left;
// background-color: #f9f9f9;
scale: 1;
transition: scale 0.15s ease;
&-icon {
width: 20px;
aspect-ratio: 1;
transform: translateY(1px);
fill: #000;
path {
fill: #000 !important;
}
}
&-name {
margin-left: 5px;
}
&:hover {
scale: 1.1;
}
}
.aplayer-list-top-buttons {
display: inline-flex;
position: absolute;
right: 10px;
align-items: center;
justify-content: center;
scale: 1;
transition: scale 0.15s ease;
&-icon {
width: 20px;
aspect-ratio: 1;
}
&:hover {
scale: 1.1;
}
}
}
</style> </style>
<style lang="scss"> <style lang="scss">
@@ -221,11 +423,10 @@ onMounted(() => {
} }
} }
.aplayer .aplayer-list ol li:hover { .aplayer .aplayer-list .miaoMusic-ol-isDragging li:hover {
background: #efefef !important; background: #efefef !important;
} }
.miaoMusicDrag-move, .miaoMusicDrag-move,
.miaoMusicDrag-enter-active, .miaoMusicDrag-enter-active,
.miaoMusicDrag-leave-active { .miaoMusicDrag-leave-active {
@@ -0,0 +1,11 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 29 32">
<path
d="M9.333 9.333h13.333v4l5.333-5.333-5.333-5.333v4h-16v8h2.667v-5.333zM22.667 22.667h-13.333v-4l-5.333 5.333 5.333 5.333v-4h16v-8h-2.667v5.333z"></path>
</svg>
</template>
<script setup lang="ts">
</script>
<style scoped></style>
+25
View File
@@ -0,0 +1,25 @@
<template>
<div class="miaoMusic-main">
<div class="fd" v-if="false">
{{ fakeData }}
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import apType from '../types/ap'
const props = defineProps<{
ap: apType
}>()
const fakeData = ref(new Array(10000).fill('我会一直盯着你'))
console.log(props.ap)
</script>
<style lang="scss" scoped>
.miaoMusic-main {
padding-bottom: 88px;
}
</style>
+18 -14
View File
@@ -9,6 +9,7 @@ export type audioType = {
} }
type apType = { type apType = {
paused: boolean
list: { list: {
audios: audioType[] audios: audioType[]
index: number index: number
@@ -16,25 +17,28 @@ type apType = {
remove: (index: number) => void remove: (index: number) => void
add: (audios: audioType[]) => void add: (audios: audioType[]) => void
refreshAudioIndex: () => void refreshAudioIndex: () => void
hide: () => void
} }
container: HTMLDivElement container: HTMLDivElement
on: (event: string, callback: () => void) => void on: (event: string, callback: () => void) => void
options: { options: {
audio: audioType[], audio: audioType[]
autoplay: boolean, autoplay: boolean
"volume": number, volume: number
"theme": string, theme: string
"mini": boolean, mini: boolean
"fixed": boolean, fixed: boolean
"mutex": boolean, mutex: boolean
"lrcType": number, lrcType: number
"preload": "auto", preload: 'auto'
"loop": "all", loop: 'all' | 'one' | 'none'
"order": "list", order: 'list' | 'random'
"listMaxHeight": string, listMaxHeight: string
"storageName": "aplayer-setting" storageName: 'aplayer-setting'
}, }
destroy: () => {} destroy: () => {}
pause: () => void
play: () => void
} }
export default apType export default apType
+3 -2
View File
@@ -15,14 +15,15 @@ const pluginConfig: registerComponentOption = {
return false return false
} }
for (let i of vFiles) { for (let i of vFiles) {
for (let s of ['mp4', 'mp3']) { for (let s of ['mp4']) {
if (i.name.endsWith(s)) { if (i.name.endsWith(s)) {
return true return true
} }
} }
} }
return false return false
} },
priority: 6
} }
export default pluginConfig export default pluginConfig
@@ -11,7 +11,7 @@ import { VirtualFile } from '@/class/VirtualDirectory'
import miaoDropHandler from '@/components/miaoDropHandler.vue' import miaoDropHandler from '@/components/miaoDropHandler.vue'
import { onBeforeUnmount, onMounted, ref } from 'vue' import { onBeforeUnmount, onMounted, ref } from 'vue'
import XGPlayer from 'xgplayer' import XGPlayer from 'xgplayer'
import '@/style/xgplayer.scss' import './style/xgplayer.scss'
import TextTrack from 'xgplayer/es/plugins/track' import TextTrack from 'xgplayer/es/plugins/track'
import 'xgplayer/es/plugins/track/index.css' import 'xgplayer/es/plugins/track/index.css'
import axios from 'axios' import axios from 'axios'
@@ -25,6 +25,7 @@
z-index: 10; z-index: 10;
background-image: linear-gradient(180deg, transparent, rgba(0, 0, 0, 0.37), rgba(0, 0, 0, 0.75), rgba(0, 0, 0, 0.75)); background-image: linear-gradient(180deg, transparent, rgba(0, 0, 0, 0.37), rgba(0, 0, 0, 0.75), rgba(0, 0, 0, 0.75));
transition: opacity 0.5s ease, visibility 0.5s ease; transition: opacity 0.5s ease, visibility 0.5s ease;
cursor: default;
} }
.xgplayer .xgplayer-controls.show { .xgplayer .xgplayer-controls.show {
display: block; display: block;
+67 -64
View File
@@ -5,69 +5,72 @@ import { resolve } from "path";
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({
base: "./", base: './',
plugins: [ plugins: [
vue() vue()
// viteSingleFile() // viteSingleFile()
], ],
resolve: { resolve: {
alias: { alias: {
"@": resolve(__dirname, "src") '@': resolve(__dirname, 'src')
} }
}, },
server: { server: {
proxy: { proxy: {
"/get": { '/get': {
target: "http://127.0.0.1:17705", target: 'http://127.0.0.1:17705',
changeOrigin: true, changeOrigin: true,
rewrite: path => path.replace(/^\/get/, "/get") rewrite: (path) => path.replace(/^\/get/, '/get')
}, },
"/upload": { '/upload': {
target: "http://127.0.0.1:17705", target: 'http://127.0.0.1:17705',
changeOrigin: true, changeOrigin: true,
rewrite: path => path.replace(/^\/upload/, "/upload") rewrite: (path) => path.replace(/^\/upload/, '/upload')
}, },
"/delete": { '/delete': {
target: "http://127.0.0.1:17705", target: 'http://127.0.0.1:17705',
changeOrigin: true, changeOrigin: true,
rewrite: path => path.replace(/^\/delete/, "/delete") rewrite: (path) => path.replace(/^\/delete/, '/delete')
}, },
"/query": { '/query': {
target: "http://127.0.0.1:17705", target: 'http://127.0.0.1:17705',
changeOrigin: true, changeOrigin: true,
rewrite: path => path.replace(/^\/query/, "/query") rewrite: (path) => path.replace(/^\/query/, '/query')
}, },
"/rename": { '/rename': {
target: "http://127.0.0.1:17705", target: 'http://127.0.0.1:17705',
changeOrigin: true, changeOrigin: true,
rewrite: path => path.replace(/^\/rename/, "/rename") rewrite: (path) => path.replace(/^\/rename/, '/rename')
}, },
"/cut": { '/cut': {
target: "http://127.0.0.1:17705", target: 'http://127.0.0.1:17705',
changeOrigin: true, changeOrigin: true,
rewrite: path => path.replace(/^\/cut/, "/cut") rewrite: (path) => path.replace(/^\/cut/, '/cut')
}, },
"/mkdir": { '/mkdir': {
target: "http://127.0.0.1:17705", target: 'http://127.0.0.1:17705',
changeOrigin: true, changeOrigin: true,
rewrite: path => path.replace(/^\/mkdir/, "/mkdir") rewrite: (path) => path.replace(/^\/mkdir/, '/mkdir')
} }
} }
}, },
build: { build: {
rollupOptions: { rollupOptions: {
input: { input: {
main: "index.html" main: 'index.html'
}, },
output: { output: {
manualChunks(id) { entryFileNames: 'assets/[name]-[hash].js',
if (id.includes("export.ts") && id.includes('plugins')) { chunkFileNames: 'assets/chunk-[name]-[hash].js',
return "pluginExports"; assetFileNames: 'assets/[name]-[hash][extname]',
} manualChunks(id) {
} if (id.includes('export.ts') && id.includes('plugins')) {
} return 'pluginExports'
} }
} }
}); }
}
}
})