You've already forked SmartisanNote.Remake
Merge pull request 'future' (#13) from future into main
Reviewed-on: yuantao/SmartisanNote.Remake#13
This commit is contained in:
@@ -88,6 +88,10 @@
|
||||
--white-60: #ffffff99; /* 60% white */
|
||||
--white-80: #ffffffcc; /* 80% white */
|
||||
--white-90: #ffffffe6; /* 90% white */
|
||||
|
||||
--confirmFontSize: 0.8rem;
|
||||
--confirmBg: rgba(0, 0, 0, 0.15);
|
||||
--confirmBtnColor: #000000cc;
|
||||
}
|
||||
|
||||
body {
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
"moment": "^2.30.1",
|
||||
"pinia": "^3.0.3",
|
||||
"vue": "^3.5.22",
|
||||
"vue-draggable-plus": "^0.6.0",
|
||||
"vue-router": "^4.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
14
src/App.vue
14
src/App.vue
@@ -13,19 +13,17 @@
|
||||
</template>
|
||||
|
||||
<!-- 设置页面 -->
|
||||
<transition
|
||||
name="settings-slide"
|
||||
v-show="isSettingsRoute"
|
||||
appear>
|
||||
<transition name="settings-slide" v-show="isSettingsRoute" appear>
|
||||
<SettingsPage class="setting-page" />
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, computed } from 'vue'
|
||||
import { ref, watch, computed, onMounted } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import '@/common/base.css'
|
||||
import { initModalService } from '@/utils/modalService'
|
||||
|
||||
// 导入页面组件
|
||||
import NoteListPage from './pages/NoteListPage.vue'
|
||||
@@ -33,6 +31,7 @@ import SettingsPage from './pages/SettingsPage.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const transitionName = ref('slide-left')
|
||||
const modalRef = ref()
|
||||
|
||||
// 计算是否为设置页面路由
|
||||
const isSettingsRoute = computed(() => {
|
||||
@@ -73,7 +72,10 @@ watch(
|
||||
}
|
||||
)
|
||||
|
||||
// 无额外处理函数
|
||||
// 初始化弹框服务
|
||||
onMounted(() => {
|
||||
initModalService()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
@@ -1,9 +1,21 @@
|
||||
<template>
|
||||
<div @click="onPress" class="code-fun-flex-row code-fun-items-center code-fun-relative folder-item">
|
||||
<img class="folder-icon" :src="iconSrc" />
|
||||
<div @click="onPress" class="code-fun-flex-row code-fun-items-center code-fun-relative code-fun-justify-between folder-item">
|
||||
<div class="code-fun-flex-row code-fun-items-center">
|
||||
<!-- 文件夹图标或复选框 -->
|
||||
<div v-if="isSelectionMode && showDeleteButton" class="folder-checkbox" @click.stop="onCheckboxClick">
|
||||
<img :src="isChecked ? '/assets/icons/drawable-xxhdpi/check_box_on.png' : '/assets/icons/drawable-xxhdpi/check_box_off.png'" class="checkbox-icon" />
|
||||
</div>
|
||||
|
||||
<img v-else class="folder-icon" :src="iconSrc" />
|
||||
<span class="folder-name">{{ name }}</span>
|
||||
<span class="folder-count">{{ noteCount }}</span>
|
||||
</div>
|
||||
|
||||
<!-- 编辑按钮,仅在选择模式下对自定义文件夹显示 -->
|
||||
<div v-if="showDeleteButton && isSelectionMode" class="folder-actions">
|
||||
<img class="edit-icon" src="/assets/icons/drawable-xxhdpi/icon_folder_rename.png" @click.stop="onEdit" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -14,41 +26,83 @@ const props = defineProps({
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
|
||||
noteCount: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
|
||||
onPress: {
|
||||
type: Function,
|
||||
required: true,
|
||||
},
|
||||
|
||||
onEdit: {
|
||||
type: Function,
|
||||
default: null,
|
||||
},
|
||||
|
||||
onDelete: {
|
||||
type: Function,
|
||||
default: null,
|
||||
},
|
||||
|
||||
isSelected: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
|
||||
isSelectionMode: {
|
||||
type: Boolean,
|
||||
|
||||
default: false,
|
||||
},
|
||||
|
||||
isChecked: {
|
||||
type: Boolean,
|
||||
|
||||
default: false,
|
||||
},
|
||||
|
||||
onCheckboxClick: {
|
||||
type: Function,
|
||||
|
||||
default: null,
|
||||
},
|
||||
})
|
||||
|
||||
const iconSrc = computed(() => {
|
||||
switch (props.id) {
|
||||
case 'all':
|
||||
return 'assets/icons/drawable-xxhdpi/icon_folder_all.png'
|
||||
|
||||
case 'starred':
|
||||
return 'assets/icons/drawable-xxhdpi/icon_folder_favorite.png'
|
||||
|
||||
case 'trash':
|
||||
return 'assets/icons/drawable-xxhdpi/icon_folder_trash.png'
|
||||
|
||||
case 'archive':
|
||||
return 'assets/icons/drawable-xxhdpi/icon_folder_document.png'
|
||||
|
||||
default:
|
||||
return 'assets/icons/drawable-xxhdpi/icon_folder_document.png'
|
||||
}
|
||||
})
|
||||
|
||||
// 仅对自定义文件夹显示删除按钮
|
||||
|
||||
const showDeleteButton = computed(() => {
|
||||
return !['all', 'starred', 'trash', 'archive'].includes(props.id)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
<style lang="less" scoped>
|
||||
.folder-item {
|
||||
padding: 0.3rem 0;
|
||||
background-color: #00000000;
|
||||
@@ -74,4 +128,41 @@ const iconSrc = computed(() => {
|
||||
margin-top: 0.2rem;
|
||||
color: #b8b8b8;
|
||||
}
|
||||
|
||||
.folder-actions {
|
||||
display: flex;
|
||||
margin-left: auto;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.edit-icon,
|
||||
.delete-icon {
|
||||
width: 1.5rem;
|
||||
|
||||
height: 1.5rem;
|
||||
|
||||
margin: 0 0.2rem;
|
||||
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.edit-icon:hover,
|
||||
.delete-icon:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.folder-checkbox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 1.8rem;
|
||||
height: 1.8rem;
|
||||
flex-shrink: 0;
|
||||
margin-inline: 0.3rem;
|
||||
}
|
||||
|
||||
.checkbox-icon {
|
||||
width: 70%;
|
||||
height: 70%;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,23 +1,52 @@
|
||||
<template>
|
||||
<div class="code-fun-flex-col page">
|
||||
<!-- 全部便签文件夹项 -->
|
||||
|
||||
<FolderItem id="all" name="全部便签" :noteCount="allCount" :isSelected="selectedFolder === 'all'" :onPress="handleAllClick" />
|
||||
|
||||
<!-- 加星便签文件夹项 -->
|
||||
|
||||
<FolderItem id="starred" name="加星便签" :noteCount="starredCount" :isSelected="selectedFolder === 'starred'" :onPress="handleStarredClick" />
|
||||
|
||||
<!-- 回收站文件夹项 -->
|
||||
|
||||
<FolderItem id="trash" name="回收站" :noteCount="trashCount" :isSelected="selectedFolder === 'trash'" :onPress="handleTrashClick" />
|
||||
|
||||
<!-- 同步信息区域 -->
|
||||
<!-- 自定义文件夹项 -->
|
||||
|
||||
<FolderItem
|
||||
v-for="folder in customFolders"
|
||||
:key="folder.id"
|
||||
:id="folder.id"
|
||||
:name="folder.name"
|
||||
:noteCount="getFolderNoteCount(folder.id)"
|
||||
:isSelected="selectedFolder === folder.id"
|
||||
:onPress="() => handleFolderClick(folder.id)"
|
||||
:onEdit="() => handleEditFolder(folder.id)"
|
||||
:onDelete="() => handleDeleteFolder(folder.id)"
|
||||
:isSelectionMode="isSelectionMode"
|
||||
:isChecked="selectedFolders.includes(folder.id)"
|
||||
:onCheckboxClick="() => toggleFolderSelection(folder.id)" />
|
||||
|
||||
<div class="code-fun-flex-col code-fun-justify-start section_7">
|
||||
<div class="code-fun-flex-row code-fun-justify-between code-fun-items-center section_8">
|
||||
<div class="code-fun-flex-row code-fun-items-center">
|
||||
<img class="code-fun-shrink-0 image_7" :src="`assets/icons/drawable-xxhdpi/btn_edit_folder.png`" />
|
||||
<!-- 切换文件夹选择模式按钮 -->
|
||||
|
||||
<img class="code-fun-shrink-0 image_7" :src="isSelectionMode ? 'assets/icons/drawable-xxhdpi/btn_back_black.png' : 'assets/icons/drawable-xxhdpi/btn_edit_folder.png'" @click="toggleSelectionMode" />
|
||||
|
||||
<!-- 同步信息区域 -->
|
||||
|
||||
<span class="text_11 code-fun-ml-10">上次同步:{{ lastSyncTime }}</span>
|
||||
</div>
|
||||
|
||||
<div class="code-fun-flex-row code-fun-items-center">
|
||||
<img class="image_8 code-fun-ml-12" :src="`assets/icons/drawable-xxhdpi/btn_add_folder.png`" @click="handleAddFolder" />
|
||||
<!-- 新建文件夹按钮 / 删除按钮 -->
|
||||
|
||||
<img
|
||||
class="image_8 code-fun-ml-12"
|
||||
:src="isSelectionMode ? 'assets/icons/drawable-xxhdpi/icon_folder_trash.png' : 'assets/icons/drawable-xxhdpi/btn_add_folder.png'"
|
||||
@click="isSelectionMode ? handleDeleteSelectedFolders() : handleAddFolder()" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -25,9 +54,12 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { ref, computed, nextTick } from 'vue'
|
||||
import { useAppStore } from '../stores/useAppStore'
|
||||
import FolderItem from './FolderItem.vue'
|
||||
import { showConfirm, showPrompt } from '../utils/modalService'
|
||||
|
||||
const store = useAppStore()
|
||||
const props = defineProps({
|
||||
allCount: {
|
||||
type: Number,
|
||||
@@ -73,37 +105,192 @@ const props = defineProps({
|
||||
type: Function,
|
||||
default: null,
|
||||
},
|
||||
onFolderClick: {
|
||||
type: Function,
|
||||
default: null,
|
||||
},
|
||||
})
|
||||
|
||||
// 重命名文件夹相关状态
|
||||
|
||||
const showRenameFolderModal = ref(false)
|
||||
|
||||
const renameFolderId = ref(null)
|
||||
|
||||
const renameFolderName = ref('')
|
||||
// 选择模式相关状态
|
||||
|
||||
const isSelectionMode = ref(false)
|
||||
|
||||
const selectedFolders = ref([])
|
||||
// 计算自定义文件夹(排除系统文件夹)
|
||||
const customFolders = computed(() => {
|
||||
return store.folders.filter(folder => !['all', 'starred', 'trash', 'archive'].includes(folder.id))
|
||||
})
|
||||
|
||||
// 获取文件夹中的便签数量
|
||||
const getFolderNoteCount = folderId => {
|
||||
return store.notes.filter(note => note.folderId === folderId && !note.isDeleted).length
|
||||
}
|
||||
|
||||
// 切换选择模式
|
||||
|
||||
const toggleSelectionMode = () => {
|
||||
isSelectionMode.value = !isSelectionMode.value
|
||||
// 退出选择模式时清空选中项
|
||||
if (!isSelectionMode.value) {
|
||||
selectedFolders.value = []
|
||||
}
|
||||
}
|
||||
|
||||
// 切换文件夹选中状态
|
||||
const toggleFolderSelection = folderId => {
|
||||
if (!isSelectionMode.value) return
|
||||
const index = selectedFolders.value.indexOf(folderId)
|
||||
if (index > -1) {
|
||||
// 已选中,取消选中
|
||||
selectedFolders.value.splice(index, 1)
|
||||
} else {
|
||||
// 未选中,添加选中
|
||||
selectedFolders.value.push(folderId)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理文件夹点击
|
||||
|
||||
const handleFolderClick = folderId => {
|
||||
if (isSelectionMode.value) {
|
||||
toggleFolderSelection(folderId)
|
||||
} else {
|
||||
if (props.onFolderClick) {
|
||||
props.onFolderClick(folderId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理删除选中的文件夹
|
||||
|
||||
const handleDeleteSelectedFolders = async () => {
|
||||
if (selectedFolders.value.length === 0) return
|
||||
|
||||
try {
|
||||
const confirmed = await showConfirm(`确定要删除选中的 ${selectedFolders.value.length} 个文件夹吗?文件夹中的便签将移至"全部便签"。`, '删除文件夹')
|
||||
|
||||
if (confirmed) {
|
||||
// 删除选中的文件夹
|
||||
|
||||
for (const folderId of selectedFolders.value) {
|
||||
// 跳过系统文件夹
|
||||
|
||||
if (['all', 'starred', 'trash', 'archive'].includes(folderId)) continue
|
||||
|
||||
await store.deleteFolder(folderId)
|
||||
}
|
||||
|
||||
// 清空选中项并退出选择模式
|
||||
|
||||
selectedFolders.value = []
|
||||
|
||||
isSelectionMode.value = false
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('删除文件夹失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理删除文件夹
|
||||
|
||||
const handleDeleteFolder = async folderId => {
|
||||
// 阻止事件冒泡到父元素
|
||||
event.stopPropagation()
|
||||
|
||||
// 确认删除
|
||||
try {
|
||||
const confirmed = await showConfirm(`确定要删除文件夹 "${getFolderName(folderId)}" 吗?文件夹中的便签将移至"全部便签"。`, '删除文件夹')
|
||||
if (confirmed) {
|
||||
await store.deleteFolder(folderId)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('删除文件夹失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理编辑文件夹
|
||||
|
||||
const handleEditFolder = async folderId => {
|
||||
// 阻止事件冒泡到父元素
|
||||
|
||||
event.stopPropagation()
|
||||
|
||||
const folder = store.folders.find(f => f.id === folderId)
|
||||
|
||||
if (folder) {
|
||||
try {
|
||||
const newName = await showPrompt('请输入文件夹名称', '重命名文件夹', '请输入文件夹名称', folder.name)
|
||||
|
||||
if (newName && newName.trim()) {
|
||||
await store.updateFolder(folderId, { name: newName.trim() })
|
||||
}
|
||||
} catch (error) {
|
||||
// 用户取消操作或出现错误
|
||||
|
||||
console.log('重命名文件夹操作已取消或出现错误:', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取文件夹名称
|
||||
|
||||
const getFolderName = folderId => {
|
||||
const folder = store.folders.find(f => f.id === folderId)
|
||||
return folder ? folder.name : ''
|
||||
}
|
||||
|
||||
const handleAllClick = () => {
|
||||
if (isSelectionMode.value) {
|
||||
toggleFolderSelection('all')
|
||||
} else {
|
||||
if (props.onAllClick) {
|
||||
props.onAllClick()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleStarredClick = () => {
|
||||
if (isSelectionMode.value) {
|
||||
toggleFolderSelection('starred')
|
||||
} else {
|
||||
if (props.onStarredClick) {
|
||||
props.onStarredClick()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleTrashClick = () => {
|
||||
if (isSelectionMode.value) {
|
||||
toggleFolderSelection('trash')
|
||||
} else {
|
||||
if (props.onTrashClick) {
|
||||
props.onTrashClick()
|
||||
}
|
||||
}
|
||||
|
||||
const handleArchiveClick = () => {
|
||||
if (props.onArchiveClick) {
|
||||
props.onArchiveClick()
|
||||
}
|
||||
}
|
||||
|
||||
const handleAddFolder = event => {
|
||||
// 阻止事件冒泡到父元素
|
||||
event.stopPropagation()
|
||||
if (props.onAddFolder) {
|
||||
props.onAddFolder()
|
||||
const handleAddFolder = async () => {
|
||||
try {
|
||||
const folderName = await showPrompt('请输入文件夹名称', '添加文件夹', '请输入文件夹名称')
|
||||
if (folderName && folderName.trim()) {
|
||||
const newFolder = {
|
||||
name: folderName.trim(),
|
||||
id: `folder_${Date.now()}`, // 生成唯一ID
|
||||
createdAt: new Date().toISOString(),
|
||||
}
|
||||
|
||||
await store.addFolder(newFolder)
|
||||
}
|
||||
} catch (error) {
|
||||
// 用户取消操作或出现错误
|
||||
console.log('添加文件夹操作已取消或出现错误:', error)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -112,21 +299,34 @@ const handleAddFolder = event => {
|
||||
.page {
|
||||
.section_7 {
|
||||
margin-top: 2rem;
|
||||
|
||||
background-color: #00000000;
|
||||
|
||||
.section_8 {
|
||||
padding: 0.29rem 0.92rem;
|
||||
|
||||
background-color: #f4f4f4;
|
||||
|
||||
border: solid 0.063rem #f0ece7;
|
||||
|
||||
.image_7,
|
||||
.image_8 {
|
||||
border-radius: 0.63rem;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
|
||||
width: 2.2rem;
|
||||
|
||||
height: 1.75rem;
|
||||
|
||||
object-fit: contain;
|
||||
|
||||
background: url(/assets/icons/drawable-xxhdpi/folder_bottom_button_normal.9.png), #fdfbfb;
|
||||
}
|
||||
|
||||
.text_11 {
|
||||
color: #cacaca;
|
||||
|
||||
font-size: 0.7rem;
|
||||
|
||||
line-height: 1.16rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,206 +1,347 @@
|
||||
<template>
|
||||
<div class="code-fun-flex-col code-fun-justify-start">
|
||||
<div class="code-fun-flex-col code-fun-justify-start group_1">
|
||||
<div class="code-fun-flex-col code-fun-justify-start code-fun-items-end group">
|
||||
<div class="code-fun-flex-col code-fun-justify-start code-fun-items-start code-fun-relative group_2">
|
||||
<div class="code-fun-flex-col section_7">
|
||||
<div class="code-fun-flex-col code-fun-justify-start section_1">
|
||||
<div class="code-fun-flex-row section_11">
|
||||
<div class="code-fun-flex-col code-fun-justify-start section_12" @click="handleCancel">
|
||||
<div class="code-fun-flex-col code-fun-justify-start code-fun-items-center text-wrapper_2">
|
||||
<span class="text_4">{{ cancelText }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="code-fun-flex-col code-fun-justify-start section_13 code-fun-ml-8" @click="handleConfirm">
|
||||
<div class="code-fun-flex-col code-fun-justify-start code-fun-items-center text-wrapper_3">
|
||||
<span class="text_5">{{ confirmText }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="code-fun-flex-col code-fun-justify-start code-fun-relative section_9">
|
||||
<div class="code-fun-flex-col code-fun-justify-start section_10">
|
||||
<div class="code-fun-flex-col code-fun-justify-start code-fun-items-start text-wrapper">
|
||||
<input
|
||||
v-model="inputValue"
|
||||
class="text_3"
|
||||
:placeholder="placeholder"
|
||||
@keyup.enter="handleConfirm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="code-fun-flex-col section_8 pos">
|
||||
<div class="code-fun-flex-col code-fun-justify-start code-fun-items-start code-fun-self-stretch group_3">
|
||||
<span class="text_2">{{ title }}</span>
|
||||
</div>
|
||||
<div class="code-fun-self-start divider"></div>
|
||||
</div>
|
||||
<div v-if="visible" class="pd-mask" @click="handleMaskClick">
|
||||
<div class="pd-confirm" @click.stop>
|
||||
<h2 class="pd-title">{{ dynamicTitle || title }}</h2>
|
||||
|
||||
<div class="pd-input-container" v-if="dynamicShowInput || showInput">
|
||||
<input ref="inputRef" v-model="inputModel" class="pd-input" :placeholder="dynamicInputPlaceholder || inputPlaceholder" @keyup.enter="handleConfirm" />
|
||||
</div>
|
||||
|
||||
<p class="pd-message" v-else>{{ dynamicMessage || message }}</p>
|
||||
|
||||
<div class="pd-buttons">
|
||||
<button v-if="dynamicShowConfirm || showConfirm" class="pd-button pd-confirm-btn" @click="handleConfirm">
|
||||
{{ dynamicConfirmText || confirmText }}
|
||||
</button>
|
||||
|
||||
<button v-if="dynamicShowCancel || showCancel" class="pd-button pd-cancel" @click="handleCancel">
|
||||
{{ dynamicCancelText || cancelText }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { ref, watch, nextTick, onMounted, onUnmounted } from 'vue'
|
||||
|
||||
// 响应式数据用于动态更新 Modal 内容
|
||||
|
||||
const dynamicTitle = ref('')
|
||||
|
||||
const dynamicMessage = ref('')
|
||||
|
||||
const dynamicConfirmText = ref('确认')
|
||||
|
||||
const dynamicCancelText = ref('取消')
|
||||
|
||||
const dynamicShowConfirm = ref(true)
|
||||
|
||||
const dynamicShowCancel = ref(true)
|
||||
|
||||
const dynamicMaskClosable = ref(false)
|
||||
|
||||
const dynamicShowInput = ref(false)
|
||||
|
||||
const dynamicInputPlaceholder = ref('请输入文字')
|
||||
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: '新建文件夹'
|
||||
default: '',
|
||||
},
|
||||
placeholder: {
|
||||
message: {
|
||||
type: String,
|
||||
default: '新建文件夹'
|
||||
default: '',
|
||||
},
|
||||
confirmText: {
|
||||
type: String,
|
||||
default: '确定'
|
||||
default: '确认',
|
||||
},
|
||||
cancelText: {
|
||||
type: String,
|
||||
default: '取消'
|
||||
}
|
||||
});
|
||||
default: '取消',
|
||||
},
|
||||
showConfirm: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
showCancel: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
maskClosable: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
showInput: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
inputPlaceholder: {
|
||||
type: String,
|
||||
default: '请输入文字',
|
||||
},
|
||||
inputValue: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(['confirm', 'cancel']);
|
||||
const emit = defineEmits(['confirm', 'cancel', 'update:visible'])
|
||||
const visible = defineModel('visible', { type: Boolean, default: false })
|
||||
const inputModel = defineModel('inputValue', { type: String, default: '' })
|
||||
|
||||
const inputValue = ref('');
|
||||
const inputRef = ref()
|
||||
|
||||
// Promise 控制变量
|
||||
|
||||
let resolvePromise, rejectPromise
|
||||
|
||||
// 返回 Promise 的方法,模拟原生 confirm 行为
|
||||
|
||||
const show = (options = {}) => {
|
||||
// 更新动态数据
|
||||
|
||||
if (options.title !== undefined) dynamicTitle.value = options.title
|
||||
|
||||
if (options.message !== undefined) dynamicMessage.value = options.message
|
||||
|
||||
if (options.confirmText !== undefined) dynamicConfirmText.value = options.confirmText
|
||||
|
||||
if (options.cancelText !== undefined) dynamicCancelText.value = options.cancelText
|
||||
|
||||
if (options.showConfirm !== undefined) dynamicShowConfirm.value = options.showConfirm
|
||||
|
||||
if (options.showCancel !== undefined) dynamicShowCancel.value = options.showCancel
|
||||
|
||||
if (options.maskClosable !== undefined) dynamicMaskClosable.value = options.maskClosable
|
||||
|
||||
if (options.showInput !== undefined) dynamicShowInput.value = options.showInput
|
||||
|
||||
if (options.inputPlaceholder !== undefined) dynamicInputPlaceholder.value = options.inputPlaceholder
|
||||
|
||||
if (options.inputValue !== undefined) inputModel.value = options.inputValue
|
||||
|
||||
// 显示对话框
|
||||
|
||||
visible.value = true
|
||||
|
||||
emit('update:visible', true)
|
||||
|
||||
// 返回 Promise
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
resolvePromise = resolve
|
||||
|
||||
rejectPromise = reject
|
||||
})
|
||||
}
|
||||
|
||||
const handleConfirm = () => {
|
||||
emit('confirm', inputValue.value);
|
||||
};
|
||||
emit('confirm')
|
||||
visible.value = false
|
||||
emit('update:visible', false)
|
||||
|
||||
const handleCancel = () => {
|
||||
emit('cancel');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.group_1 {
|
||||
padding-bottom: 1rem;
|
||||
.group {
|
||||
padding: 0.5rem 0 0.25rem;
|
||||
.group_2 {
|
||||
margin-right: 0.75rem;
|
||||
width: 32.31rem;
|
||||
.section_7 {
|
||||
padding-top: 5.75rem;
|
||||
background-color: #00000000;
|
||||
width: 29.06rem;
|
||||
height: 18.16rem;
|
||||
background-image: url(https://codefun-proj-user-res-1256085488.cos.ap-guangzhou.myqcloud.com/686f20ecd54496f19f54e801/68e862ab9520a30011f388ff/17600601480475170758.png);
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% auto;
|
||||
background-position: 0% 0%;
|
||||
.section_1 {
|
||||
margin-top: 7.88rem;
|
||||
background-color: #00000000;
|
||||
.section_11 {
|
||||
padding: 0.5rem 0.5rem 0.75rem;
|
||||
background-color: #f4f4f7;
|
||||
border-radius: 0rem 0rem 0.75rem 0.75rem;
|
||||
border: solid 0.032rem #edeee8;
|
||||
.section_12 {
|
||||
padding: 0.25rem 0;
|
||||
background-color: #00000000;
|
||||
width: 13.63rem;
|
||||
height: 3.13rem;
|
||||
.text-wrapper_2 {
|
||||
margin: 0 0.25rem;
|
||||
padding: 0.75rem 0;
|
||||
background-color: #f2f2f2;
|
||||
border-radius: 0.25rem;
|
||||
width: 13.34rem;
|
||||
border: solid 0.032rem #d7d7d7;
|
||||
.text_4 {
|
||||
color: #757575;
|
||||
font-size: 1.23rem;
|
||||
font-weight: 700;
|
||||
line-height: 1.23rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
.section_13 {
|
||||
margin-right: 0.25rem;
|
||||
padding-top: 0.25rem;
|
||||
background-color: #00000000;
|
||||
width: 13.59rem;
|
||||
height: 3.06rem;
|
||||
.text-wrapper_3 {
|
||||
margin-left: 0.25rem;
|
||||
padding: 0.75rem 0;
|
||||
border-radius: 0.25rem;
|
||||
background-image: url('assets/53062683132af1946e1a4953530af228.png');
|
||||
background-size: 100% 100%;
|
||||
background-repeat: no-repeat;
|
||||
width: 13.34rem;
|
||||
.text_5 {
|
||||
color: #cddff2;
|
||||
font-size: 1.19rem;
|
||||
line-height: 1.19rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.section_9 {
|
||||
margin-top: -12.5rem;
|
||||
padding: 1.75rem 0 0.25rem;
|
||||
background-color: #00000000;
|
||||
.section_10 {
|
||||
margin: 0 1.5rem;
|
||||
padding-top: 0.25rem;
|
||||
background-color: #00000000;
|
||||
width: 25.88rem;
|
||||
.text-wrapper {
|
||||
margin: 0 0.25rem;
|
||||
padding: 0.75rem 0;
|
||||
background-color: #fefefe;
|
||||
border-radius: 0.25rem;
|
||||
width: 25.53rem;
|
||||
border: solid 0.063rem #e1e1e1;
|
||||
.text_3 {
|
||||
margin-left: 1rem;
|
||||
color: #d0d0d0;
|
||||
font-size: 1.38rem;
|
||||
font-weight: 700;
|
||||
line-height: 1.38rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.section_8 {
|
||||
padding: 0 0.13rem;
|
||||
background-color: #00000000;
|
||||
width: 32.31rem;
|
||||
.group_3 {
|
||||
padding: 1.5rem 0;
|
||||
width: 32.06rem;
|
||||
.text_2 {
|
||||
margin-left: 10.5rem;
|
||||
color: #7a7a7a;
|
||||
font-size: 1.54rem;
|
||||
font-weight: 700;
|
||||
line-height: 1.54rem;
|
||||
}
|
||||
}
|
||||
.divider {
|
||||
background-color: #f0f0f0;
|
||||
width: 29rem;
|
||||
height: 0.094rem;
|
||||
}
|
||||
}
|
||||
.pos {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
// 解决 Promise,传递输入框的值
|
||||
if (resolvePromise) {
|
||||
resolvePromise(inputModel.value)
|
||||
resolvePromise = null
|
||||
rejectPromise = null
|
||||
}
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
emit('cancel')
|
||||
visible.value = false
|
||||
emit('update:visible', false)
|
||||
|
||||
// 拒绝 Promise
|
||||
if (rejectPromise) {
|
||||
rejectPromise()
|
||||
resolvePromise = null
|
||||
rejectPromise = null
|
||||
}
|
||||
}
|
||||
|
||||
const handleMaskClick = () => {
|
||||
if (props.maskClosable) {
|
||||
handleCancel() // 点击遮罩相当于取消
|
||||
}
|
||||
}
|
||||
|
||||
// 添加/移除 body 滚动锁定
|
||||
const lockBodyScroll = () => {
|
||||
document.body.style.overflow = 'hidden'
|
||||
}
|
||||
|
||||
const unlockBodyScroll = () => {
|
||||
document.body.style.overflow = ''
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (visible.value) {
|
||||
lockBodyScroll()
|
||||
}
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
unlockBodyScroll()
|
||||
})
|
||||
|
||||
// 监听 visible 变化
|
||||
watch(visible, (newVal, oldVal) => {
|
||||
if (newVal) {
|
||||
lockBodyScroll()
|
||||
// 如果模态框包含输入框,则自动获取焦点
|
||||
if (dynamicShowInput.value || props.showInput) {
|
||||
// 使用 nextTick 确保在 DOM 更新后聚焦
|
||||
nextTick(() => {
|
||||
// 添加更长的延迟确保动画完成
|
||||
setTimeout(() => {
|
||||
// 确保 inputRef 存在并且可见
|
||||
if (inputRef.value && inputRef.value.offsetParent !== null) {
|
||||
inputRef.value.focus()
|
||||
inputRef.value.select() // 选中所有文本,提升用户体验
|
||||
}
|
||||
}, 100)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
unlockBodyScroll()
|
||||
// 重置输入框引用,确保下次打开时能正确获取焦点
|
||||
if (inputRef.value) {
|
||||
inputRef.value.blur()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 暴露 show 方法给父组件使用
|
||||
defineExpose({ show })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.pd-mask {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 999999999;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: inherit;
|
||||
background: var(--confirmBg, inherit);
|
||||
}
|
||||
|
||||
.pd-confirm {
|
||||
background: inherit;
|
||||
background: var(--confirmTheme, #fff);
|
||||
text-align: center;
|
||||
color: var(--confirmColor, #636363);
|
||||
font-size: var(--confirmFontSize, 1rem);
|
||||
max-width: 75vw;
|
||||
min-width: 20em;
|
||||
box-shadow: 0px 35px 35px -10px rgba(0, 0, 0, 0.33);
|
||||
border-radius: 10px;
|
||||
position: relative;
|
||||
white-space: break-spaces;
|
||||
word-break: break-all;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.pd-title {
|
||||
position: relative;
|
||||
font-size: 1.3em;
|
||||
min-height: 1em;
|
||||
background: var(--confirmBtnBg, #fafafa);
|
||||
color: var(--confirmBtnColor, #636363);
|
||||
margin: 0;
|
||||
padding: 0.5em 0;
|
||||
}
|
||||
|
||||
.pd-message {
|
||||
position: relative;
|
||||
border-top: 1px solid var(--confirmBtnBorder, #f1f1f1);
|
||||
width: 100%;
|
||||
max-height: 70vh;
|
||||
border-bottom: 1px solid var(--confirmBtnBorder, #f1f1f1);
|
||||
margin: 0 auto;
|
||||
box-sizing: border-box;
|
||||
padding: 2em 10%;
|
||||
line-height: 1.4;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.pd-input-container {
|
||||
margin-block: 1.5em;
|
||||
padding: 0 10% 1em;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.pd-input {
|
||||
width: 100%;
|
||||
padding: 0.8em;
|
||||
font-size: 1em;
|
||||
border: 1px solid var(--confirmBtnBorder, #f1f1f1);
|
||||
border-radius: 4px;
|
||||
box-sizing: border-box;
|
||||
outline: none;
|
||||
background: var(--confirmTheme, #fff);
|
||||
color: var(--confirmColor, #636363);
|
||||
box-shadow: 0px 0px 4px inset rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.pd-input::placeholder {
|
||||
color: var(--black-20);
|
||||
}
|
||||
|
||||
.pd-input:focus {
|
||||
border-color: #4d90fe;
|
||||
}
|
||||
|
||||
.pd-input::selection {
|
||||
background-color: #4d90fe; /* 选中时的背景颜色 */
|
||||
color: #ffffff; /* 选中时的文字颜色 */
|
||||
}
|
||||
|
||||
.pd-buttons {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.pd-button {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
font-size: 1em;
|
||||
appearance: none;
|
||||
background: var(--confirmBtnBg, #fafafa);
|
||||
color: var(--confirmBtnColor, #636363);
|
||||
border: none;
|
||||
border-right: 1px solid var(--confirmBtnBorder, #f1f1f1);
|
||||
padding: 1em 0;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* 当同时显示确认和取消按钮时,每个按钮占50%宽度 */
|
||||
.pd-button:first-child:nth-last-child(2),
|
||||
.pd-button:first-child:nth-last-child(2) ~ .pd-button {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
/* 当只显示一个按钮时,该按钮占100%宽度 */
|
||||
.pd-button:first-child:nth-last-child(1) {
|
||||
width: 100%;
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
/* 只有最后一个按钮没有右边框 */
|
||||
.pd-button:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
slot="fixed" />
|
||||
|
||||
<!-- 悬浮文件夹列表 - 使用绝对定位实现 -->
|
||||
<transition name="folder-slide">
|
||||
<div v-if="isFolderExpanded" class="folder-list" slot="fixed">
|
||||
<FolderManage
|
||||
:allCount="allNotesCount"
|
||||
@@ -23,8 +24,11 @@
|
||||
:selectedFolder="currentFolder"
|
||||
:onAllClick="handleAllNotesClick"
|
||||
:onStarredClick="handleStarredNotesClick"
|
||||
:onTrashClick="handleTrashNotesClick" />
|
||||
:onTrashClick="handleTrashNotesClick"
|
||||
:onFolderClick="handleFolderClick"
|
||||
:onAddFolder="handleAddFolder" />
|
||||
</div>
|
||||
</transition>
|
||||
<!-- 点击外部区域收起文件夹列表的覆盖层 -->
|
||||
<div v-if="isFolderExpanded" @click="() => setIsFolderExpanded(false)" class="folder-overlay" slot="fixed"></div>
|
||||
<div class="search-container">
|
||||
@@ -166,7 +170,9 @@ const headerTitle = computed(() => {
|
||||
case 'trash':
|
||||
return '回收站'
|
||||
default:
|
||||
return '全部便签'
|
||||
// 查找自定义文件夹的名称
|
||||
const folder = store.folders.find(f => f.id === currentFolder.value)
|
||||
return folder ? folder.name : '全部便签'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -277,6 +283,17 @@ const handleTrashNotesClick = () => {
|
||||
setIsFolderExpanded(false)
|
||||
}
|
||||
|
||||
const handleFolderClick = folderId => {
|
||||
setCurrentFolder(folderId)
|
||||
setIsFolderExpanded(false)
|
||||
}
|
||||
|
||||
const handleAddFolder = () => {
|
||||
// 文件夹添加功能已在FolderManage组件中实现
|
||||
// 这里只需关闭文件夹列表
|
||||
setIsFolderExpanded(false)
|
||||
}
|
||||
|
||||
const handleFolderPress = () => {
|
||||
// 使用vue-router导航到文件夹页面
|
||||
router.push('/folders')
|
||||
@@ -427,4 +444,30 @@ const notes = computed(() => store.notes)
|
||||
position: absolute;
|
||||
width: calc(100% - 1rem);
|
||||
}
|
||||
|
||||
/* 文件夹列表动画 */
|
||||
.folder-slide-enter-active,
|
||||
.folder-slide-leave-active {
|
||||
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
}
|
||||
|
||||
.folder-slide-enter-from {
|
||||
opacity: 0;
|
||||
transform: scale(0.8) translateY(-20px);
|
||||
}
|
||||
|
||||
.folder-slide-enter-to {
|
||||
opacity: 1;
|
||||
transform: scale(1) translateY(0);
|
||||
}
|
||||
|
||||
.folder-slide-leave-from {
|
||||
opacity: 1;
|
||||
transform: scale(1) translateY(0);
|
||||
}
|
||||
|
||||
.folder-slide-leave-to {
|
||||
opacity: 0;
|
||||
transform: scale(0.8) translateY(-20px);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -295,6 +295,53 @@ export const useAppStore = defineStore('app', {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 更新文件夹
|
||||
* @param {string} id - 文件夹ID
|
||||
* @param {Object} updates - 要更新的属性对象
|
||||
* @returns {Promise<Object>} 更新后的文件夹对象
|
||||
*/
|
||||
async updateFolder(id, updates) {
|
||||
try {
|
||||
const updatedFolder = await storage.updateFolder(id, updates)
|
||||
if (updatedFolder) {
|
||||
const index = this.folders.findIndex(folder => folder.id === id)
|
||||
if (index !== -1) {
|
||||
this.folders[index] = updatedFolder
|
||||
}
|
||||
}
|
||||
return updatedFolder
|
||||
} catch (error) {
|
||||
console.error('Error updating folder:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除文件夹
|
||||
* @param {string} id - 要删除的文件夹ID
|
||||
* @returns {Promise<boolean>} 删除成功返回true,失败返回false
|
||||
*/
|
||||
async deleteFolder(id) {
|
||||
try {
|
||||
const result = await storage.deleteFolder(id)
|
||||
if (result) {
|
||||
// 将文件夹中的便签移回"全部便签"
|
||||
const notesInFolder = this.notes.filter(note => note.folderId === id)
|
||||
for (const note of notesInFolder) {
|
||||
await this.updateNote(note.id, { folderId: null })
|
||||
}
|
||||
|
||||
// 从文件夹列表中移除文件夹
|
||||
this.folders = this.folders.filter(folder => folder.id !== id)
|
||||
}
|
||||
return result
|
||||
} catch (error) {
|
||||
console.error('Error deleting folder:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 设置操作函数
|
||||
*/
|
||||
|
||||
@@ -371,6 +371,41 @@ export const addFolder = async (folder) => {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新文件夹
|
||||
* 根据ID查找并更新文件夹信息
|
||||
* @param {string} id - 文件夹ID
|
||||
* @param {Object} updates - 要更新的属性对象
|
||||
* @returns {Promise<Object|null>} 更新后的文件夹对象,如果未找到则返回null
|
||||
*/
|
||||
export const updateFolder = async (id, updates) => {
|
||||
try {
|
||||
// 更新文件夹并保存
|
||||
const updatedFolder = await updateInStore(FOLDERS_STORE, id, updates)
|
||||
return updatedFolder
|
||||
} catch (error) {
|
||||
console.error('Error updating folder:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文件夹
|
||||
* 根据ID从文件夹列表中移除文件夹
|
||||
* @param {string} id - 要删除的文件夹ID
|
||||
* @returns {Promise<boolean>} 删除成功返回true,未找到文件夹返回false
|
||||
*/
|
||||
export const deleteFolder = async (id) => {
|
||||
try {
|
||||
// 从存储中删除
|
||||
const result = await deleteFromStore(FOLDERS_STORE, id)
|
||||
return result
|
||||
} catch (error) {
|
||||
console.error('Error deleting folder:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 设置操作函数
|
||||
// 提供应用设置的读取和保存功能
|
||||
|
||||
|
||||
113
src/utils/modalService.js
Normal file
113
src/utils/modalService.js
Normal file
@@ -0,0 +1,113 @@
|
||||
import { createApp } from 'vue'
|
||||
import Modal from '@/components/Modal.vue'
|
||||
|
||||
/**
|
||||
* 全局弹框服务
|
||||
* 提供统一的弹框调用接口,使用项目中已有的 Modal 组件
|
||||
*/
|
||||
|
||||
// 创建一个全局的 Modal 实例容器
|
||||
let modalInstance = null
|
||||
let modalContainer = null
|
||||
|
||||
/**
|
||||
* 初始化 Modal 实例
|
||||
* 在应用启动时调用一次
|
||||
*/
|
||||
export const initModalService = () => {
|
||||
if (!modalContainer) {
|
||||
// 创建一个隐藏的 div 作为 Modal 的挂载点
|
||||
modalContainer = document.createElement('div')
|
||||
document.body.appendChild(modalContainer)
|
||||
|
||||
// 创建 Modal 实例
|
||||
const app = createApp(Modal)
|
||||
modalInstance = app.mount(modalContainer)
|
||||
}
|
||||
|
||||
return modalInstance
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示确认弹框
|
||||
* @param {string} message - 弹框消息
|
||||
* @param {string} title - 弹框标题
|
||||
* @param {Object} options - 弹框选项
|
||||
* @returns {Promise<boolean>} 用户确认返回 true,取消返回 false
|
||||
*/
|
||||
export const showConfirm = (message, title = '确认', options = {}) => {
|
||||
// 确保 Modal 实例已初始化
|
||||
if (!modalInstance) {
|
||||
initModalService()
|
||||
}
|
||||
|
||||
// 显示弹框
|
||||
return modalInstance.show({
|
||||
title,
|
||||
message,
|
||||
showInput: false,
|
||||
showConfirm: true,
|
||||
showCancel: true,
|
||||
...options
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示提示弹框
|
||||
* @param {string} message - 弹框消息
|
||||
* @param {string} title - 弹框标题
|
||||
* @param {Object} options - 弹框选项
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export const showAlert = (message, title = '提示', options = {}) => {
|
||||
// 确保 Modal 实例已初始化
|
||||
if (!modalInstance) {
|
||||
initModalService()
|
||||
}
|
||||
|
||||
// 显示弹框
|
||||
return modalInstance.show({
|
||||
title,
|
||||
message,
|
||||
showInput: false,
|
||||
showConfirm: true,
|
||||
showCancel: false,
|
||||
...options
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示输入弹框
|
||||
* @param {string} message - 弹框消息
|
||||
* @param {string} title - 弹框标题
|
||||
* @param {string} placeholder - 输入框占位符
|
||||
* @param {string} defaultValue - 输入框默认值
|
||||
* @param {Object} options - 弹框选项
|
||||
* @returns {Promise<string|null>} 用户输入的值,取消返回 null
|
||||
*/
|
||||
export const showPrompt = (message, title = '输入', placeholder = '请输入文字', defaultValue = '', options = {}) => {
|
||||
// 确保 Modal 实例已初始化
|
||||
if (!modalInstance) {
|
||||
initModalService()
|
||||
}
|
||||
|
||||
// 显示弹框
|
||||
return modalInstance.show({
|
||||
title,
|
||||
message,
|
||||
showInput: true,
|
||||
showConfirm: true,
|
||||
showCancel: true,
|
||||
inputPlaceholder: placeholder,
|
||||
inputValue: defaultValue,
|
||||
...options
|
||||
})
|
||||
}
|
||||
|
||||
// 默认导出所有方法
|
||||
export default {
|
||||
initModalService,
|
||||
showConfirm,
|
||||
showAlert,
|
||||
showPrompt
|
||||
}
|
||||
Reference in New Issue
Block a user