You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
199 lines
5.9 KiB
199 lines
5.9 KiB
<script lang="ts" setup>
|
|
import {getRootBarrier} from './utils'
|
|
import {dispatch} from '../worker'
|
|
import {emit} from '../../shared'
|
|
import {computed, nextTick, onMounted, ref, watch} from 'vue'
|
|
import {store} from '../store'
|
|
import UiDialog from './UiDialog.vue'
|
|
import {controller} from './UiDirectoryNamingController'
|
|
import {toast} from './UiToastController'
|
|
|
|
const barrier = ref<Node | null>()
|
|
const hasError = ref(false)
|
|
const conformClicked = ref(false)
|
|
const isSubmitting = ref(false)
|
|
const content = ref('')
|
|
|
|
const title = computed(() => {
|
|
if (controller.action === 'rename') {
|
|
return '重命名文件夹'
|
|
} else if (controller.pid) {
|
|
return "创建子文件夹"
|
|
} else {
|
|
return "创建文件夹"
|
|
}
|
|
})
|
|
|
|
const parent = computed(() => {
|
|
return controller.pid
|
|
? store.directories.get(controller.pid)
|
|
: undefined
|
|
})
|
|
|
|
const directory = computed(() => {
|
|
return controller.id
|
|
? store.directories.get(controller.id)
|
|
: undefined
|
|
})
|
|
|
|
const handleCancel = (): void => {
|
|
if (!isSubmitting.value) {
|
|
controller.visible = false
|
|
nextTick(() => {
|
|
hasError.value = false
|
|
conformClicked.value = false
|
|
isSubmitting.value = false
|
|
content.value = ''
|
|
})
|
|
}
|
|
}
|
|
|
|
const handleConfirm = (): void => {
|
|
if (isSubmitting.value) {
|
|
return;
|
|
}
|
|
if (content.value.trim().length < 2) {
|
|
hasError.value = true
|
|
}
|
|
if (hasError.value) {
|
|
return;
|
|
}
|
|
if (!conformClicked.value) {
|
|
conformClicked.value = true
|
|
return
|
|
}
|
|
isSubmitting.value = true
|
|
requestAnimationFrame(async () => {
|
|
try {
|
|
const title = content.value.trim()
|
|
switch (controller.action) {
|
|
case "create": {
|
|
const pid = parent.value?.id ?? 0
|
|
const result = await dispatch("dir", "create", {title, pid});
|
|
store.directories.set(result.id, result);
|
|
toast("success", `成功创建文件夹 “${title}”`)
|
|
if (pid && pid === controller.pid) {
|
|
emit("directory:expanded", parent.value!.id)
|
|
}
|
|
break
|
|
}
|
|
case "rename": {
|
|
const dir = store.directories.get(directory.value!.id);
|
|
if (!dir) {
|
|
toast("error", "文件夹不存在")
|
|
break
|
|
}
|
|
const res = await dispatch("dir", "rename", {id: dir.id, title});
|
|
Object.assign(dir, res);
|
|
toast("success", `成功将文件夹 “${directory.value!.title}” 重命名为 “${title}”`)
|
|
break
|
|
}
|
|
}
|
|
isSubmitting.value = false
|
|
handleCancel()
|
|
} catch (err) {
|
|
isSubmitting.value = false
|
|
toast('error', err as any)
|
|
}
|
|
})
|
|
}
|
|
|
|
watch(() => controller.visible, (value) => {
|
|
if (value && controller.action === 'rename') {
|
|
content.value = directory.value?.title ?? ''
|
|
}
|
|
})
|
|
|
|
watch(content, (value) => {
|
|
conformClicked.value = false
|
|
if (value.trim().length > 1) {
|
|
hasError.value = false
|
|
}
|
|
})
|
|
|
|
onMounted(() => {
|
|
barrier.value = getRootBarrier()
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<UiDialog
|
|
v-if="barrier"
|
|
:barrier="barrier"
|
|
:visible="controller.visible"
|
|
class="w-96 rounded-md overflow-hidden shadow-2xl"
|
|
outside="shake"
|
|
@dismissed="handleCancel"
|
|
>
|
|
<div class="bg-white p-6">
|
|
<h3
|
|
id="modal-title"
|
|
class="text-base font-semibold leading-6 text-gray-900"
|
|
v-text="title"
|
|
/>
|
|
<p class="text-xs text-gray-500 mt-1">
|
|
<template v-if="controller.action === 'rename'">
|
|
您正在修改文件夹 “<b
|
|
class="text-black align-baseline text-sm">{{ directory!.title }}</b>”;<br/>
|
|
</template>
|
|
<template v-else-if="controller.pid">
|
|
您正在为 “<b
|
|
class="text-black align-baseline text-sm">{{ parent!.title }}</b>”
|
|
创建子文件夹;<br/>
|
|
</template>
|
|
<span :class="{ 'text-red-500': hasError }">
|
|
文件夹的名称至少 2 个字符,请尽量不要超过 8 个汉字。
|
|
</span>
|
|
</p>
|
|
<div class="mt-4">
|
|
<input
|
|
v-model="content"
|
|
:disabled="isSubmitting" autocomplete="off"
|
|
class="block w-full rounded-md border-0 px-4 py-2 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6 disabled:bg-gray-100 disabled:cursor-progress"
|
|
maxlength="20" minlength="2"
|
|
placeholder="请输入文件夹名称" required
|
|
@blur="hasError = content.length < 2"/>
|
|
</div>
|
|
</div>
|
|
<div
|
|
class="bg-gray-50 px-6 py-3 flex justify-end gap-4 border-t border-t-slate-200 text-sm font-semibold">
|
|
<button
|
|
:disabled="isSubmitting"
|
|
class="rounded-md px-4 py-2 select-none transition-all bg-white text-gray-900 shadow-sm hover:bg-gray-50 ring-1 ring-inset ring-gray-300 disabled:bg-gray-100 disabled:hover:bg-gray-100 disabled:cursor-progress"
|
|
type="button"
|
|
@click="handleCancel"
|
|
>
|
|
取消
|
|
</button>
|
|
<button
|
|
:disabled="isSubmitting"
|
|
class="inline-flex items-center gap-2 px-4 py-2 rounded-md transition-all select-none bg-indigo-600 text-white shadow-sm hover:bg-indigo-500 disabled:bg-indigo-400 disabled:hover:bg-indigo-400 disabled:cursor-progress"
|
|
type="button"
|
|
@click="handleConfirm"
|
|
>
|
|
<svg
|
|
v-if="isSubmitting"
|
|
class="animate-spin h-3 w-3 text-white"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
>
|
|
<circle
|
|
class="opacity-25"
|
|
cx="12"
|
|
cy="12"
|
|
r="10"
|
|
stroke="currentColor"
|
|
stroke-width="4"
|
|
/>
|
|
<path
|
|
class="opacity-75"
|
|
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
fill="currentColor"
|
|
/>
|
|
</svg>
|
|
{{ conformClicked ? "确定提交?" : "提交" }}
|
|
</button>
|
|
</div>
|
|
</UiDialog>
|
|
</template>
|
|
|