feat:新增批量修改文件名页面

This commit is contained in:
czhqwer
2025-04-08 00:38:35 +08:00
parent 3223d3fd6c
commit aaa64edd4b
6 changed files with 562 additions and 22 deletions

View File

@@ -1,46 +1,228 @@
<template>
<div class="batch-rename">
<el-upload
drag
multiple
:auto-upload="false"
:on-change="handleFileChange"
>
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
</el-upload>
<el-input v-model="renameRule" placeholder="输入重命名规则" class="rename-input"></el-input>
<el-button type="primary" @click="rename" class="action-btn">执行重命名</el-button>
<div class="file-renamer">
<SelectDir v-model="filePaths" @change="handlePathChange" :showFiles="true" :allowSelectFolder="false"
:multiple="true" style="width: 100%;" />
<el-container>
<el-main>
<!-- 文件列表 -->
<el-card v-show="files.length > 0" class="mt-4">
<div slot="header" class="flex justify-between items-center">
<span>已选择的文件</span>
<el-button type="danger" size="small" @click="clearFiles" style="margin-left: 10px;">
<i class="el-icon-delete mr-1"></i>全部清除
</el-button>
</div>
<el-table :data="files" max-height="250">
<el-table-column prop="name" label="文件名" width="400"></el-table-column>
<el-table-column label="操作" width="100">
<template slot-scope="scope">
<el-button type="text" size="small" @click="removeFile(scope.$index)">
<i class="el-icon-close text-red-500"></i>
</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<!-- 重命名选项 -->
<el-card class="mt-4">
<el-tabs v-model="activeTab">
<el-tab-pane label="插入/替换" name="insert">
<InsertTab />
</el-tab-pane>
<el-tab-pane label="删除" name="delete">
<DeleteTab />
</el-tab-pane>
<el-tab-pane label="编号" name="numbering">
<NumberingTab />
</el-tab-pane>
<el-tab-pane label="清理" name="cleanup">
<CleanupTab />
</el-tab-pane>
<el-tab-pane label="高级" name="advanced">
<AdvancedTab />
</el-tab-pane>
</el-tabs>
</el-card>
<!-- 操作按钮组 -->
<div class="action-buttons mt-4">
<el-button size="medium" @click="reset">重置</el-button>
<el-button size="medium" type="info" @click="showPreview">预览</el-button>
<el-button size="medium" type="primary" @click="rename">重命名</el-button>
</div>
<!-- 预览对话框 -->
<el-dialog
title="重命名预览"
:visible.sync="previewDialogVisible"
width="70%"
:modal="false"
:append-to-body="true"
:before-close="handlePreviewClose">
<div class="preview-area">
<div v-if="files.length === 0" class="text-center text-gray-500 py-8">
<i class="el-icon-document text-3xl mb-2"></i>
<p>未选择文件或预览不可用</p>
</div>
<el-table v-else :data="previewData" max-height="400">
<el-table-column prop="original" label="原文件名" width="300"></el-table-column>
<el-table-column label="→" width="50" align="center"></el-table-column>
<el-table-column prop="new" label="新文件名" width="300"></el-table-column>
</el-table>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="previewDialogVisible = false">关闭</el-button>
<el-button type="primary" @click="rename">确认重命名</el-button>
</span>
</el-dialog>
</el-main>
</el-container>
</div>
</template>
<script>
import SelectDir from '@/components/SelectDir/SelectDir.vue'
import InsertTab from './BatchRename/InsertTab.vue'
import DeleteTab from './BatchRename/DeleteTab.vue'
import NumberingTab from './BatchRename/NumberingTab.vue'
import CleanupTab from './BatchRename/CleanupTab.vue'
import AdvancedTab from './BatchRename/AdvancedTab.vue'
export default {
name: 'BatchRename',
components: {
SelectDir,
InsertTab,
DeleteTab,
NumberingTab,
CleanupTab,
AdvancedTab
},
data() {
return {
renameRule: ''
files: [],
activeTab: 'insert',
isDragging: false,
filePaths: '',
previewDialogVisible: false
}
},
computed: {
previewData() {
return this.files.map(file => ({
original: file.name,
new: file.name // 这里应实现实际的重命名逻辑
}))
}
},
methods: {
handleFileChange(file, fileList) {
console.log('文件列表:', fileList)
handlePathChange(newPaths) {
this.filePaths = newPaths
// Handle both string and array inputs
const pathArray = Array.isArray(newPaths) ? newPaths : [newPaths]
const newFiles = pathArray.map(path => ({
name: path.split('\\').pop(), // Get the last part of the path as filename
path: path // Store the full path
}))
// Filter out duplicates before adding
const uniqueNewFiles = newFiles.filter(newFile =>
!this.files.some(existingFile => existingFile.path === newFile.path)
)
this.files.push(...uniqueNewFiles)
},
handleDrop(e) {
this.unhighlight()
const newFiles = [...e.dataTransfer.files]
this.addFiles(newFiles)
},
handleFileInput() {
const newFiles = [...this.$refs.fileInput.files]
this.addFiles(newFiles)
},
highlight() {
this.isDragging = true
},
unhighlight() {
this.isDragging = false
},
addFiles(newFiles) {
newFiles = newFiles.filter(newFile =>
!this.files.some(f =>
f.name === newFile.name &&
f.size === newFile.size &&
f.lastModified === newFile.lastModified
)
)
this.files = [...this.files, ...newFiles]
},
removeFile(index) {
this.files.splice(index, 1)
},
clearFiles() {
this.files = []
},
reset() {
this.$message.success('所有重命名规则已重置')
},
rename() {
this.$message.success('批量重命名功能待实现')
if (this.files.length === 0) {
this.$message.warning('请选择要重命名的文件')
return
}
this.$message.success(`将重命名 ${this.files.length} 个文件(此演示为模拟操作)`)
},
showPreview() {
if (this.files.length === 0) {
this.$message.warning('请选择要重命名的文件')
return
}
this.previewDialogVisible = true
},
handlePreviewClose(done) {
this.$confirm('确认关闭预览?')
.then(() => {
done()
})
.catch(() => {})
}
}
}
</script>
<style scoped>
.batch-rename {
padding: 10px;
.file-renamer {
padding: 20px;
max-width: 100%;
margin: 0 auto;
}
.rename-input {
margin: 15px 0;
.dropzone {
border: 2px dashed #dcdcdc;
padding: 20px;
transition: all 0.3s ease;
cursor: pointer;
}
.action-btn {
.dropzone:hover {
border-color: #409eff;
background-color: #ecf5ff;
}
.preview-area {
min-height: 200px;
}
.mt-4 {
margin-top: 10px;
}
.action-buttons {
display: flex;
justify-content: flex-end;
gap: 10px;
padding: 10px 0;
}
</style>

View File

@@ -0,0 +1,84 @@
<template>
<div class="grid grid-cols-2 gap-4">
<div>
<el-form label-width="80px">
<el-form-item label="随机字符串">
<div class="grid grid-cols-2 gap-2">
<el-form-item label="长度" label-width="40px">
<el-input-number v-model="randomLength" :min="1" :max="32" size="small"></el-input-number>
</el-form-item>
<el-form-item label="类型" label-width="40px">
<el-select v-model="randomType" size="small">
<el-option label="字母数字混合" value="alphanum"></el-option>
<el-option label="仅字母" value="alpha"></el-option>
<el-option label="仅数字" value="num"></el-option>
<el-option label="自定义字符" value="custom"></el-option>
</el-select>
</el-form-item>
</div>
<div class="mt-2">
<el-checkbox v-model="useUpper">大写</el-checkbox>
<el-checkbox v-model="useLower" class="ml-4">小写</el-checkbox>
</div>
</el-form-item>
<el-form-item label="日期/时间">
<div class="flex gap-2">
<el-select v-model="dateType" class="flex-1">
<el-option label="创建日期" value="created"></el-option>
<el-option label="修改日期" value="modified"></el-option>
<el-option label="当前日期" value="current"></el-option>
</el-select>
<el-input v-model="dateFormat" class="flex-1" placeholder="YYYY-MM-DD"></el-input>
</div>
<div class="text-xs text-gray-500 mt-1">
格式YYYYMMDDHHmmss
</div>
</el-form-item>
</el-form>
</div>
<div>
<el-form label-width="80px">
<el-form-item label="文件元数据">
<div class="flex gap-2">
<el-select v-model="metadataType" class="flex-1">
<el-option label="文件大小" value="size"></el-option>
<el-option label="MD5 哈希" value="md5"></el-option>
<el-option label="SHA-1 哈希" value="sha1"></el-option>
<el-option label="尺寸(图片)" value="dimensions"></el-option>
<el-option label="时长(媒体)" value="duration"></el-option>
</el-select>
<el-select v-model="metadataPosition" class="flex-1">
<el-option label="在开头" value="start"></el-option>
<el-option label="在结尾" value="end"></el-option>
<el-option label="替换整个名称" value="replace"></el-option>
</el-select>
</div>
</el-form-item>
<el-form-item label="JS 规则">
<el-input type="textarea" v-model="jsRule" :rows="4" placeholder="function rename(name, index) { return name.toUpperCase(); }"></el-input>
<div class="text-xs text-gray-500 mt-1">
可用变量name文件名ext扩展名index文件编号
</div>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
export default {
data() {
return {
randomLength: 8,
randomType: 'alphanum',
useUpper: false,
useLower: true,
dateType: 'created',
dateFormat: 'YYYY-MM-DD',
metadataType: 'size',
metadataPosition: 'start',
jsRule: ''
}
}
}
</script>

View File

@@ -0,0 +1,67 @@
<template>
<div class="grid grid-cols-2 gap-4">
<div>
<el-form label-width="80px">
<el-form-item label="移除内容">
<div class="flex flex-wrap gap-2">
<el-button size="small" @click="removeParentheses">移除括号 () 中的文本</el-button>
<el-button size="small" @click="removeBrackets">移除方括号 [] 中的文本</el-button>
<el-button size="small" @click="removeBraces">移除大括号 {} 中的文本</el-button>
<el-button size="small" @click="removeAllBrackets">移除所有括号</el-button>
</div>
</el-form-item>
<el-form-item label="修剪选项">
<div class="flex flex-wrap gap-2">
<el-button size="small" @click="trimSpaces">修剪空格</el-button>
<el-button size="small" @click="trimUnderscores">修剪下划线</el-button>
<el-button size="small" @click="trimDots">修剪点号</el-button>
<el-button size="small" @click="trimAll">修剪所有</el-button>
</div>
</el-form-item>
</el-form>
</div>
<div>
<el-form label-width="80px">
<el-form-item label="替换字符">
<div class="flex flex-wrap gap-2">
<el-button size="small" @click="spaceToUnderscore">空格转为下划线</el-button>
<el-button size="small" @click="underscoreToSpace">下划线转为空格</el-button>
<el-button size="small" @click="spaceToDot">空格转为点号</el-button>
<el-button size="small" @click="dotToSpace">点号转为空格</el-button>
</div>
</el-form-item>
<el-form-item label="重复字符">
<div class="flex flex-wrap gap-2">
<el-button size="small" @click="removeDupSpaces">移除重复空格</el-button>
<el-button size="small" @click="removeDupUnderscores">移除重复下划线</el-button>
<el-button size="small" @click="removeDupDots">移除重复点号</el-button>
<el-button size="small" @click="removeAllDup">移除所有重复</el-button>
</div>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
export default {
methods: {
removeParentheses() { this.$message.info('移除括号内容(模拟操作)') },
removeBrackets() { this.$message.info('移除方括号内容(模拟操作)') },
removeBraces() { this.$message.info('移除大括号内容(模拟操作)') },
removeAllBrackets() { this.$message.info('移除所有括号(模拟操作)') },
trimSpaces() { this.$message.info('修剪空格(模拟操作)') },
trimUnderscores() { this.$message.info('修剪下划线(模拟操作)') },
trimDots() { this.$message.info('修剪点号(模拟操作)') },
trimAll() { this.$message.info('修剪所有(模拟操作)') },
spaceToUnderscore() { this.$message.info('空格转为下划线(模拟操作)') },
underscoreToSpace() { this.$message.info('下划线转为空格(模拟操作)') },
spaceToDot() { this.$message.info('空格转为点号(模拟操作)') },
dotToSpace() { this.$message.info('点号转为空格(模拟操作)') },
removeDupSpaces() { this.$message.info('移除重复空格(模拟操作)') },
removeDupUnderscores() { this.$message.info('移除重复下划线(模拟操作)') },
removeDupDots() { this.$message.info('移除重复点号(模拟操作)') },
removeAllDup() { this.$message.info('移除所有重复(模拟操作)') }
}
}
</script>

View File

@@ -0,0 +1,71 @@
<template>
<div class="grid grid-cols-2 gap-4">
<div>
<el-form label-width="80px">
<el-form-item label="按文本删除">
<div class="flex gap-2">
<el-input v-model="deleteText" placeholder="要删除的文本" class="flex-1"></el-input>
<el-select v-model="deleteOption" class="flex-1">
<el-option label="全部删除" value="all"></el-option>
<el-option label="仅首次" value="first"></el-option>
<el-option label="仅最后" value="last"></el-option>
</el-select>
</div>
</el-form-item>
<el-form-item label="按位置删除">
<div class="flex gap-2 items-center">
<el-input-number v-model="startPos" :min="1" size="small" placeholder="起始"></el-input-number>
<span></span>
<el-input-number v-model="endPos" :min="1" size="small" placeholder="结束"></el-input-number>
<span>字符</span>
</div>
</el-form-item>
</el-form>
</div>
<div>
<el-form label-width="80px">
<el-form-item label="删除文件名">
<el-checkbox v-model="keepExt">保留扩展名</el-checkbox>
<el-button type="danger" size="small" class="ml-2" @click="deleteAllNames">删除所有名称</el-button>
</el-form-item>
<el-form-item label="删除扩展名">
<div class="flex gap-2">
<el-button size="small" @click="removeAllExt">移除所有扩展名</el-button>
<el-input v-model="specificExt" placeholder=".ext" style="width: 100px;"></el-input>
<el-button size="small" @click="removeSpecificExt">移除特定扩展名</el-button>
</div>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
export default {
data() {
return {
deleteText: '',
deleteOption: 'all',
startPos: 1,
endPos: 1,
keepExt: true,
specificExt: ''
}
},
methods: {
deleteAllNames() {
this.$message.info('删除所有名称(模拟操作)')
},
removeAllExt() {
this.$message.info('移除所有扩展名(模拟操作)')
},
removeSpecificExt() {
if (!this.specificExt) {
this.$message.warning('请输入要移除的扩展名')
return
}
this.$message.info(`移除扩展名 ${this.specificExt}(模拟操作)`)
}
}
}
</script>

View File

@@ -0,0 +1,61 @@
<template>
<div class="grid grid-cols-2 gap-4">
<div>
<el-form label-width="80px">
<el-form-item label="插入文本">
<div class="flex gap-2">
<el-select v-model="insertPosition" class="flex-1">
<el-option label="在开头" value="start"></el-option>
<el-option label="在结尾" value="end"></el-option>
<el-option label="在指定位置" value="position"></el-option>
</el-select>
<el-input v-model="insertText" placeholder="要插入的文本" class="flex-1"></el-input>
</div>
</el-form-item>
<el-form-item label="替换文本">
<el-input v-model="searchText" placeholder="查找" class="mb-2"></el-input>
<el-input v-model="replaceText" placeholder="替换为" class="mb-2"></el-input>
<el-radio-group v-model="replaceOption">
<el-radio label="all">全部替换</el-radio>
<el-radio label="first">仅首次</el-radio>
<el-radio label="last">仅最后</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</div>
<div>
<el-form label-width="80px">
<el-form-item label="正则表达式">
<el-input v-model="regexPattern" placeholder="正则模式" class="mb-2"></el-input>
<el-input v-model="regexReplace" placeholder="替换为 ($1, $2...)" class="mb-2"></el-input>
<div class="text-xs text-gray-500">
使用 $1, $2 等引用捕获组例如"(.*)\.(.*)" "$2.$1" 可交换点前后的部分
</div>
</el-form-item>
<el-form-item label="大小写选项">
<el-button-group>
<el-button size="small">全大写</el-button>
<el-button size="small">全小写</el-button>
<el-button size="small">标题格式</el-button>
</el-button-group>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
export default {
data() {
return {
insertPosition: 'start',
insertText: '',
searchText: '',
replaceText: '',
replaceOption: 'all',
regexPattern: '',
regexReplace: ''
}
}
}
</script>

View File

@@ -0,0 +1,75 @@
<template>
<div class="grid grid-cols-2 gap-4">
<div>
<el-form label-width="80px">
<el-form-item label="编号选项">
<div class="grid grid-cols-2 gap-2">
<el-form-item label="起始数字" label-width="60px">
<el-input-number v-model="startNum" :min="0" size="small"></el-input-number>
</el-form-item>
<el-form-item label="步长" label-width="60px">
<el-input-number v-model="step" :min="1" size="small"></el-input-number>
</el-form-item>
<el-form-item label="重复次数" label-width="60px">
<el-input-number v-model="repeat" :min="1" size="small"></el-input-number>
</el-form-item>
<el-form-item label="位数" label-width="60px">
<el-input-number v-model="digits" :min="1" :max="10" size="small"></el-input-number>
</el-form-item>
</div>
</el-form-item>
<el-form-item label="数字位置">
<div class="flex gap-2">
<el-select v-model="numberPosition" class="flex-1">
<el-option label="在开头" value="start"></el-option>
<el-option label="在结尾" value="end"></el-option>
<el-option label="在指定位置" value="position"></el-option>
<el-option label="替换整个名称" value="replace"></el-option>
</el-select>
<el-input-number v-model="position" :min="1" size="small" v-if="numberPosition === 'position'"></el-input-number>
</div>
</el-form-item>
</el-form>
</div>
<div>
<el-form label-width="80px">
<el-form-item label="填充选项">
<el-checkbox v-model="padZero">用零填充</el-checkbox>
<el-input v-model="padChar" maxlength="1" size="small" class="ml-2" style="width: 50px;" v-if="padZero"></el-input>
</el-form-item>
<el-form-item label="数字格式">
<div class="flex gap-2">
<el-input v-model="format" class="flex-1"></el-input>
<el-button type="primary" size="small" @click="previewNumber">预览</el-button>
</div>
<div class="text-xs text-gray-500 mt-1">
使用 {n} 表示数字{name} 表示原文件名{ext} 表示扩展名
</div>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
export default {
data() {
return {
startNum: 1,
step: 1,
repeat: 1,
digits: 1,
numberPosition: 'start',
position: 1,
padZero: true,
padChar: '0',
format: '{n}'
}
},
methods: {
previewNumber() {
this.$message.info('预览数字格式(模拟操作)')
}
}
}
</script>