File.split(pathname)

本文深入解析Ruby中用于文件操作的类,包括如何通过split、atime、ctime、mtime等方法进行文件信息的获取和修改。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

http://www.kuqin.com/rubycndocument/man/built-in-class/class_object_io_file.html

 

 

File.split(pathname)<!-- RDLabel: "File.split" -->

pathname分为dirnamebasename,并返回一个包含这两部分的数组。它与

[File.dirname(pathname), File.basename(pathname)]

相同。

 

 

 

访问文件用的类。通常使用openFile.open来生成。

超类:<!-- RDLabel: "超类:" -->

包含的模块:<!-- RDLabel: "包含的模块:" -->

  • File::Constants

ruby 1.8 特性: File::Constants已被包含在IO之中。

类方法:<!-- RDLabel: "类方法:" -->

File.atime(filename)<!-- RDLabel: "File.atime" --> File.ctime(filename)<!-- RDLabel: "File.ctime" --> File.mtime(filename)<!-- RDLabel: "File.mtime" -->

它们分别返回:文件的最终访问时间/文件状态的最终变更时间/最终更新时间(Time对象)。

若未能成功取得文件时间则引发Errno::EXXX异常。

File.basename(filename[, suffix])<!-- RDLabel: "File.basename" -->

返回filename中的最后一条斜线后面的部分。若给出了参数suffix且它和filename的尾部一致时,该方法会将其删除并返回结果。

例:

p File.basename("ruby/ruby.c")        #=> "ruby.c"
p File.basename("ruby/ruby.c", ".c")  #=> "ruby"
p File.basename("ruby/ruby.c", ".*")  #=> "ruby"
p File.basename("ruby/ruby.exe", ".*")  #=> "ruby"

另外,请参考File.dirnameFile.extname

从1.6.8到1.8.0版本间的修改(小结)

修改之后,File.basename的动作遵循SUSv3的规定。

p File.basename("foo/bar/")      # => "bar"  以前是""
File.chmod(mode[, filename[, ...]])<!-- RDLabel: "File.chmod" --> File.lchmod(mode[, filename[, ...]]) ((<ruby 1.7 特性>))<!-- RDLabel: "File.lchmod" -->

将文件的模式改为mode。它返回修改文件的个数。若修改模式失败则引发Errno::EXXX异常。

modechmod(2)一样,使用数值来指定其内容。

lchmod只修改符号连接中的连接本身的模式。在未安装lchmod(2)的系统中调用该方法时会引发NotImplementedError异常。

File.chown(owner, group[, filename[, ...]])<!-- RDLabel: "File.chown" --> File.lchown(owner, group[, filename[, ...]]) ((<ruby 1.7 特性>))<!-- RDLabel: "File.lchown" -->

修改文件的owner和group。只有超级用户才能修改文件的owner和group。它返回修改文件的个数。若修改失败则引发Errno::EXXX异常。

ownergroupchown(2)一样,使用数值来指定其内容。若只想修改其中之一时,可以将不想修改的一方指定为nil或-1,这样就可以保持原状了。

lchown只修改符号连接中的连接本身的owner和group。在未安装lchown(2)的系统中调用该方法时会引发NotImplementedError异常。

File.delete(filename ... )<!-- RDLabel: "File.delete" --> File.unlink(filename ... )<!-- RDLabel: "File.unlink" -->

删除文件。返回删除文件的个数。若删除失败则引发Errno::EXXX异常。

该方法是用来删除普通文件的。而删除目录时要使用Dir.rmdir

File.dirname(filename)<!-- RDLabel: "File.dirname" -->

以字符串的形式返回filename中最后一条斜线之前的部分。若文件名中不含斜线,则返回"."(当前目录)。

例:

p File.dirname("dir/file.ext")    # => "dir"
p File.dirname("file.ext")        # => "."

另外,请参考File.basename, File.extname

ruby 1.8 特性

修改后,File.dirname的动作遵守SUSv3的规定。

p File.dirname("foo/bar/")      # => "foo"  以前是、"foo/bar"
p File.dirname("foo//bar")      # => "foo"  以前是、"foo/"
File.expand_path(path[, default_dir])<!-- RDLabel: "File.expand_path" -->

path展开为绝对路径后返回该路径字符串。若path是相对路径则以default_dir为基准进行扩展。若没有default_dir或其值为nil时将使用当前目录。

开头的~会被扩展为主目录(使用环境变量HOME),而~USER会被扩展为相应用户的主目录。

p File.expand_path("..")         #=> "/home/matz/work"
p File.expand_path("..", "/tmp") #=> "/"
p File.expand_path("~")          #=> "/home/matz"
p File.expand_path("~foo")       #=> "/home/foo"
File.extname(filename) ((<ruby 1.7 特性>))<!-- RDLabel: "File.extname" -->

返回文件名filename中的扩展名部分(跟在最后的"."之后的部分)。目录名中的"."和文件名头上的"."不会被看作扩展名的一部分。若filename中不含扩展名则返回空字符串。

p File.extname("foo/foo.txt")     # => ".txt"
p File.extname("foo/foo.tar.gz")  # => ".gz"
p File.extname("foo/bar")         # => ""
p File.extname("foo/.bar")        # => ""
p File.extname("foo.txt/bar")     # => ""
p File.extname(".foo")            # => ""

请参考File.basename, File.dirname

File.fnmatch(pattern, path[, flags]) ((<ruby 1.7 特性>))<!-- RDLabel: "File.fnmatch" --> File.fnmatch?(pattern, path[, flags]) ((<ruby 1.7 特性>))<!-- RDLabel: "File.fnmatch?" -->

对文件名进行模式匹配(fnmatch(3))。若pathpattern相匹配则返回真。

pattern中可使用的通配符有`*', `?'和`[]'(与Dir.glob不同的是,这里不能使用`{}' 或 `**/')。

%w(foo foobar bar).each {|f|
  p File.fnmatch("foo*", f)
}
# => true
     true
     false

flags中可以任意使用下列常数(定义在File::Constants模块中)中的若干个来改变模式匹配的运作情况。flags的默认值为0(不指定标识)。

FNM_NOESCAPE<!-- RDLabel: "FNM_NOESCAPE" -->

将转义字符`\'看作普通字符。

# 默认情况下,带\的字符都会匹配该字符本身,
# 加上此标识后,\被当作普通字符处理。
p File.fnmatch('\a', 'a')                       # => true
p File.fnmatch('\a', '\a', File::FNM_NOESCAPE)  # => true

# 在前者中,*发生了转义,只会匹配"*"字符
# 本身。
p File.fnmatch('\*', 'a')                       # => false
p File.fnmatch('\*', '\a', File::FNM_NOESCAPE)  # => true

# 单独的\则不受此标识的影响,一律匹配于\。
# (请注意,在单引号内的字符串中,\\就是\)
p File.fnmatch('\\', '\\')                      # => true
p File.fnmatch('\\', '\\', File::FNM_NOESCAPE)  # => true
FNM_PATHNAME<!-- RDLabel: "FNM_PATHNAME" -->

通配符`*', `?', `[]'将不再匹配`/'。主要作用在shell的模式匹配中。

p File.fnmatch('*', '/', File::FNM_PATHNAME)   # => false
p File.fnmatch('?', '/', File::FNM_PATHNAME)   # => false
p File.fnmatch('[/]', '/', File::FNM_PATHNAME) # => false
FNM_CASEFOLD<!-- RDLabel: "FNM_CASEFOLD" -->

进行匹配时,不区分大小写字母。

p File.fnmatch('A', 'a', File::FNM_CASEFOLD)   # => true
FNM_DOTMATCH<!-- RDLabel: "FNM_DOTMATCH" -->

通配符`*', `?', `[]'将匹配于开头的`.'。

p File.fnmatch('*', '.', File::FNM_DOTMATCH)           # => true
p File.fnmatch('?', '.', File::FNM_DOTMATCH)           # => true
p File.fnmatch('[.]', '.', File::FNM_DOTMATCH)         # => true
p File.fnmatch('foo/*', 'foo/.', File::FNM_DOTMATCH)   # => true
File.ftype(filename)<!-- RDLabel: "File.ftype" -->

返回表示文件类型的字符串。该字符串应为下列之一。与File.lstat(filename).ftype相同(若为符号连接则返回"link")。

"file"
"directory"
"characterSpecial"
"blockSpecial"
"fifo"
"link"
"socket"

"unknown"
File.join(item, item, ...)<!-- RDLabel: "File.join" -->

File::SEPARATOR置入其中连成字符串。它与

[item, item, ...].join(File::SEPARATOR)

相同。(此后可能提供DOSISH处理功能,但它依赖于系统环境。)

File.link(old, new)<!-- RDLabel: "File.link" -->

生成一个指向old且名为new的硬连接(hard link)。old必须是现存的。

若成功生成硬连接就返回0。若失败则引发Errno::EXXX异常。

File.new(path[, mode [, perm]])<!-- RDLabel: "File.new" --> File.open(path[, mode [, perm]])<!-- RDLabel: "File.open" --> File.open(path[, mode [, perm]]) {|file| ... }<!-- RDLabel: "File.open" -->

打开path所指文件并返回文件对象。若打开文件失败时引发Errno::EXXX异常。

参数mode, perm的用法与内部函数open相同。

open()可以带块。若带块调用时,将把文件对象传给块,然后开始块的运行。当块终止运行时,文件将被自动关闭。

带块调用时的返回值是块的执行结果。

File.readlink(path)<!-- RDLabel: "File.readlink" -->

以字符串形式返回字符连接的连接对象。若读取连接失败则引发Errno::EXXX异常。

File.rename(from, to)<!-- RDLabel: "File.rename" -->

修改文件名称。若目录不同时,将进行移动。请参考rename(2)。若目标目录中有同名文件则会进行覆盖。

若成功移动文件就返回0。若失败则引发Errno::EXXX异常。

File.split(pathname)<!-- RDLabel: "File.split" -->

pathname分为dirnamebasename,并返回一个包含这两部分的数组。它与

[File.dirname(pathname), File.basename(pathname)]

相同。

File.stat(filename)<!-- RDLabel: "File.stat" --> File.lstat(filename)<!-- RDLabel: "File.lstat" -->

生成并返回一个包含filename信息的File::Stat对象。若获取信息失败则引发Errno::EXXX异常。

lstat返回符号连接中的连接本身的信息。在未安装lstat(2)的系统中,它与File.stat相同。

请参考IO#stat,File#lstat

File.symlink(old, new)<!-- RDLabel: "File.symlink" -->

生成一个指向old且名为new的符号连接。

若成功生成符号连接则返回0。若失败则引发Errno::EXXX异常。

File.truncate(path, length)<!-- RDLabel: "File.truncate" -->

path所指文件裁减为至多length字节的文件。

若成功更改大小时返回0。若失败则引发Errno::EXXX异常。

File.umask([umask])<!-- RDLabel: "File.umask" -->

修改umask。返回修改前的umask值。若省略umask则不修改,返回当前的umask值。

File.utime(atime, mtime[, filename[, ...]])<!-- RDLabel: "File.utime" -->

修改文件的最终访问时间和更新时间。返回修改的文件数。若修改失败则引发Errno::EXXX异常。

开始的两个参数必须是,表示时间的数值或Time类的实例。

File.blockdev?(path)<!-- RDLabel: "File.blockdev?" -->

FileTest.blockdev?相同。

File.chardev?(path)<!-- RDLabel: "File.chardev?" -->

FileTest.chardev?相同。

File.directory?(path)<!-- RDLabel: "File.directory?" -->

FileTest.directory?相同。

File.executable?(path)<!-- RDLabel: "File.executable?" -->

FileTest.executable?相同。

File.executable_real?(path)<!-- RDLabel: "File.executable_real?" -->

FileTest.executable_real?相同。

File.exist?(path)<!-- RDLabel: "File.exist?" -->

FileTest.exist?相同。

File.file?(path)<!-- RDLabel: "File.file?" -->

FileTest.file?相同。

File.grpowned?(path)<!-- RDLabel: "File.grpowned?" -->

FileTest.grpowned?相同。

File.owned?(path)<!-- RDLabel: "File.owned?" -->

FileTest.owned?相同。

File.pipe?(path)<!-- RDLabel: "File.pipe?" -->

FileTest.pipe?相同。

File.readable?(path)<!-- RDLabel: "File.readable?" -->

FileTest.readable?相同。

File.readable_real?(path)<!-- RDLabel: "File.readable_real?" -->

FileTest.readable_real?相同。

File.setgid?(path)<!-- RDLabel: "File.setgid?" -->

FileTest.setgid?相同。

File.setuid?(path)<!-- RDLabel: "File.setuid?" -->

FileTest.setuid?相同。

File.size(path)<!-- RDLabel: "File.size" -->

FileTest.size相同。

File.size?(path)<!-- RDLabel: "File.size?" -->

FileTest.size?相同。

File.socket?(path)<!-- RDLabel: "File.socket?" -->

FileTest.socket相同。

File.sticky?(path)<!-- RDLabel: "File.sticky?" -->

FileTest.sticky?相同。

File.symlink?(path)<!-- RDLabel: "File.symlink?" -->

FileTest.symlink?相同。

File.writable?(path)<!-- RDLabel: "File.writable?" -->

FileTest.writable?相同。

File.writable_real?(path)<!-- RDLabel: "File.writable_real?" -->

FileTest.writable_real?相同。

File.zero?(path)<!-- RDLabel: "File.zero?" -->

FileTest.zero?相同。

方法:<!-- RDLabel: "方法:" -->

atime<!-- RDLabel: "atime" --> ctime<!-- RDLabel: "ctime" --> mtime<!-- RDLabel: "mtime" -->

它们分别返回:文件的最终访问时间/文件状态的最终变更时间/最终更新时间(Time对象)。

若未能成功取得文件时间则引发Errno::EXXX异常。

chmod(mode)<!-- RDLabel: "chmod" -->

将文件模式修改为mode.若修改成功则返回0.若失败则引发Errno::EXXX异常.

modechmod(2)一样,使用数值来指定其内容.

chown(owner, group)<!-- RDLabel: "chown" -->

修改文件的owner和group.只有超级用户才能修改文件的owner和group.修改成功则返回0,失败则引发Errno::EXXX异常.

owner,groupchown(2)一样,使用数值来指定其内容.若将其指定为nil或-1的话,就可以保留现在的状态.

flock(operation)<!-- RDLabel: "flock" -->

锁定文件. 锁定成功时返回0. 失败则引发Errno::EXXX异常. 若在LOCK_NB状态下发生阻塞(block)则返回false.下列是可用的operation列表.

  • LOCK_SH

    共享锁. 若干个进程可同时共享锁定.

    根据系统的不同,有时要求锁定对象必须是以读取模式("r","r+"等)打开的才行. 在这些系统中,若对不可读文件进行锁定时,可能会引发Errno::EBADF异常.

  • LOCK_EX

    排它锁.在某时刻,只有一个进程能控制锁.

    根据系统的不同,有时要求锁定对象必须是以写入模式("w","r+"等)打开的才行. 在这些系统中,若对不可写文件进行锁定时,可能会引发Errno::EBADF异常.

  • LOCK_UN

    解锁.

    除了这种显式的解锁以外, 当Ruby解释器结束(进程结束)时,也会自动解锁.

  • LOCK_NB

    非阻塞模式.

    LOCK_SH | LOCK_NB一样,它可以借用or与其他operation一起使用. 若没有指定LOCK_NB的话,若在阻塞条件下调用flock时,该阻塞将会一直持续到解锁时.

    若指定了LOCK_NB的话,若在阻塞时调用flock则返回false.

    所谓"阻塞时"是指发生下述情况

    • 在其他进程已设置排它锁的情况下进行锁定
    • 在其他进程已经锁定的情况下设置排它锁

    的时候.

上述常数都被定义在模块中.

例:

f = File.open("/tmp/foo", "w")

f.flock(File::LOCK_EX)
puts "locked by process1"

fork {
  f = File.open("/tmp/foo", "r")
  f.flock(File::LOCK_SH)
  puts "locked by process2"
  sleep 5
  puts "unlocked by process2"
}

sleep 5

f.flock(File::LOCK_UN)
puts "unlocked by process1"
sleep 1 # <- 为确保子进程能够抢先进行锁定而进行的 sleep
f.flock(File::LOCK_EX)
puts "re-locked by process1"

=> locked by process1
   unlocked by process1
   locked by process2
   unlocked by process2
   re-locked by process1
path<!-- RDLabel: "path" -->

以字符串形式返回打开文件时使用的路径.

lstat<!-- RDLabel: "lstat" -->

生成并返回一个包含文件状态信息的File::Stat对象.若未能成功取得文件信息则引发Errno::EXXX异常.

lstat只返回符号连接中的连接本身的信息. 在那些未安装lstat(2)的系统中,它与IO#stat相同.

另外,请参考IO#stat,File.statFile.lstat.

truncate(length)<!-- RDLabel: "truncate" -->

将文件裁剪成至多length字节大小的文件. 若文件并不是以写入模式打开的话,将引发IOError异常.

若修改成功则返回0,若失败则引发Errno::EXXX异常.

常数:<!-- RDLabel: "常数:" -->

ALT_SEPARATOR<!-- RDLabel: "ALT_SEPARATOR" -->

当系统的文件路径分隔符与SEPARATOR不同时,即可使用该常数.在MS-DOS等系统中是"\",而在UNIX 或Cygwin等系统中则是nil.

PATH_SEPARATOR<!-- RDLabel: "PATH_SEPARATOR" -->

PATH环境变量的元素分隔符.在UNIX中是":",而在MS-DOS等系统中是";".

SEPARATOR<!-- RDLabel: "SEPARATOR" --> Separator<!-- RDLabel: "Separator" -->

文件路径的分隔符. 例如,在将路径名传给处理文件的方法时就会用到它. 该分隔符使得脚本内的路径名的格式统一且不受环境的影响.其值为"/".

内部类:<!-- RDLabel: "内部类:" -->

Constants<!-- RDLabel: "Constants" -->

包括File类的相关常数的模块. 请参考File::Constants.

Stat<!-- RDLabel: "Stat" -->

表示stat结构体(请参考stat(2))的类. 请参考File::Stat.

 

 

好的,我明白你的意思了,我的插件也是通过点击来获取文件中diff,但是这样会导致diff页面跳转到对应点击文件。所有是否有办法拿到对应文件的diff而不进行跳转呢 下面是插件异步处理方法 // 温和的异步点击获取diff(进一步避免页面跳动) async function gentleAsyncClickToLoadDiff(file, maxWaitMs = 8000) { console.log(`[温和异步点击] 开始温和异步点击获取文件 ${file.fileName} 的diff`); // 保存当前页面状态 const currentScrollPosition = window.scrollY; const currentActiveElement = document.activeElement; const currentSelection = window.getSelection().toString(); console.log(`[温和异步点击] 保存当前页面状态: scrollY=${currentScrollPosition}, activeElement=${currentActiveElement?.tagName}`); // 检查是否有匹配的diff(文件名必须完全匹配) const exactMatch = fileDiffMap[file.fileName]; if (exactMatch) { console.log(`[温和异步点击] 文件 ${file.fileName} 已有完全匹配的diff缓存`); return exactMatch; } // 检查是否有智能匹配的diff const availableFiles = Object.keys(fileDiffMap); const matchingDiffFile = availableFiles.find(availableFile => { if (availableFile === '__single__') return false; const pageFileName = file.fileName; const apiFileName = availableFile; const getFileName = (path) => path.split('/').pop().split('\\').pop(); const pageFileBase = getFileName(pageFileName); const apiFileBase = getFileName(apiFileName); const matches = [ pageFileName === apiFileName, pageFileBase === apiFileBase, apiFileName.includes(pageFileBase), pageFileName.includes(apiFileBase), pageFileName.includes(apiFileName) || apiFileName.includes(pageFileName) ]; return matches.some(match => match); }); if (matchingDiffFile) { console.log(`[温和异步点击] 文件 ${file.fileName} 找到智能匹配的diff: ${matchingDiffFile}`); return fileDiffMap[matchingDiffFile]; } // 清空之前的diff缓存,确保获取到当前文件的diff const previousDiffMap = { ...fileDiffMap }; fileDiffMap = {}; console.log(`[温和异步点击] 清空diff缓存,准备加载 ${file.fileName} 的diff`); try { // 使用更温和的异步点击方式,进一步避免页面跳动 console.log(`[温和异步点击] 使用更温和的异步点击方式: ${file.fileName}`); // 方法1: 使用更长的延迟和更温和的方式 const titleElement = file.element.querySelector('.teamix-title'); if (titleElement) { console.log(`[温和异步点击] 找到文件标题元素:`, titleElement.tagName, titleElement.className); // 使用更长的延迟和更温和的方式 return new Promise((resolve) => { const gentleCallback = () => { // 使用更温和的点击方式 try { // 方法1: 使用dispatchEvent而不是click() const clickEvent = new MouseEvent('click', { bubbles: true, cancelable: true, view: window, detail: 1, screenX: 0, screenY: 0, clientX: 0, clientY: 0 }); titleElement.dispatchEvent(clickEvent); console.log(`[温和异步点击] 温和点击完成: ${file.fileName}`); } catch (error) { console.warn(`[温和异步点击] 温和点击失败,尝试直接点击:`, error); titleElement.click(); } // 等待更长时间,确保diff加载完成 setTimeout(() => { // 检查是否成功加载到匹配的diff const newExactMatch = fileDiffMap[file.fileName]; if (newExactMatch) { console.log(`[温和异步点击] 成功: 已获取到 ${file.fileName} 的diff`); resolve(newExactMatch); return; } // 检查智能匹配 const newMatchingDiffFile = Object.keys(fileDiffMap).find(availableFile => { if (availableFile === '__single__') return false; const pageFileName = file.fileName; const apiFileName = availableFile; const getFileName = (path) => path.split('/').pop().split('\\').pop(); const pageFileBase = getFileName(pageFileName); const apiFileBase = getFileName(apiFileName); const matches = [ pageFileName === apiFileName, pageFileBase === apiFileBase, apiFileName.includes(pageFileBase), pageFileName.includes(apiFileBase), pageFileName.includes(apiFileName) || apiFileName.includes(pageFileName) ]; return matches.some(match => match); }); if (newMatchingDiffFile) { console.log(`[温和异步点击] 成功: 找到智能匹配的diff: ${newMatchingDiffFile}`); resolve(fileDiffMap[newMatchingDiffFile]); return; } // 如果没有找到匹配的diff,返回null console.log(`[温和异步点击] 未找到匹配的diff: ${file.fileName}`); resolve(null); }, 3000); // 增加等待时间到3秒,确保diff加载完成 }; // 使用更长的延迟,确保页面完全稳定 setTimeout(() => { if (window.requestIdleCallback) { window.requestIdleCallback(gentleCallback, { timeout: 5000 }); } else { setTimeout(gentleCallback, 500); } }, 1000); // 额外等待1秒,确保页面稳定 }); } // 方法2: 如果找不到标题元素,尝试其他可点击元素 console.log(`[温和异步点击] 方法2: 尝试其他可点击元素 ${file.fileName}`); const clickableElement = file.element.querySelector('a, button, [role="button"], span'); if (clickableElement && !clickableElement.classList.contains('next-tree-node-indent-unit')) { console.log(`[温和异步点击] 找到可点击元素:`, clickableElement.tagName, clickableElement.className); return new Promise((resolve) => { const gentleCallback = () => { try { const clickEvent = new MouseEvent('click', { bubbles: true, cancelable: true, view: window, detail: 1, screenX: 0, screenY: 0, clientX: 0, clientY: 0 }); clickableElement.dispatchEvent(clickEvent); console.log(`[温和异步点击] 温和点击可点击元素完成: ${file.fileName}`); } catch (error) { console.warn(`[温和异步点击] 温和点击失败,尝试直接点击:`, error); clickableElement.click(); } setTimeout(() => { // 检查结果 const newExactMatch = fileDiffMap[file.fileName]; if (newExactMatch) { console.log(`[温和异步点击] 方法2成功: 已获取到 ${file.fileName} 的diff`); resolve(newExactMatch); return; } const newMatchingDiffFile = Object.keys(fileDiffMap).find(availableFile => { if (availableFile === '__single__') return false; const pageFileName = file.fileName; const apiFileName = availableFile; const getFileName = (path) => path.split('/').pop().split('\\').pop(); const pageFileBase = getFileName(pageFileName); const apiFileBase = getFileName(apiFileName); const matches = [ pageFileName === apiFileName, pageFileBase === apiFileBase, apiFileName.includes(pageFileBase), pageFileName.includes(apiFileBase), pageFileName.includes(apiFileName) || apiFileName.includes(pageFileName) ]; return matches.some(match => match); }); if (newMatchingDiffFile) { console.log(`[温和异步点击] 方法2成功: 找到智能匹配的diff: ${newMatchingDiffFile}`); resolve(fileDiffMap[newMatchingDiffFile]); return; } console.log(`[温和异步点击] 方法2失败: 未找到匹配的diff: ${file.fileName}`); resolve(null); }, 3000); }; setTimeout(() => { if (window.requestIdleCallback) { window.requestIdleCallback(gentleCallback, { timeout: 5000 }); } else { setTimeout(gentleCallback, 500); } }, 1000); }); } // 方法3: 如果都找不到可点击元素,尝试直接点击文件元素 console.log(`[温和异步点击] 方法3: 尝试直接点击文件元素 ${file.fileName}`); if (file.element && typeof file.element.click === 'function') { return new Promise((resolve) => { const gentleCallback = () => { try { const clickEvent = new MouseEvent('click', { bubbles: true, cancelable: true, view: window, detail: 1, screenX: 0, screenY: 0, clientX: 0, clientY: 0 }); file.element.dispatchEvent(clickEvent); console.log(`[温和异步点击] 温和点击文件元素完成: ${file.fileName}`); } catch (error) { console.warn(`[温和异步点击] 温和点击失败,尝试直接点击:`, error); file.element.click(); } setTimeout(() => { // 检查结果 const newExactMatch = fileDiffMap[file.fileName]; if (newExactMatch) { console.log(`[温和异步点击] 方法3成功: 已获取到 ${file.fileName} 的diff`); resolve(newExactMatch); return; } const newMatchingDiffFile = Object.keys(fileDiffMap).find(availableFile => { if (availableFile === '__single__') return false; const pageFileName = file.fileName; const apiFileName = availableFile; const getFileName = (path) => path.split('/').pop().split('\\').pop(); const pageFileBase = getFileName(pageFileName); const apiFileBase = getFileName(apiFileName); const matches = [ pageFileName === apiFileName, pageFileBase === apiFileBase, apiFileName.includes(pageFileBase), pageFileName.includes(apiFileBase), pageFileName.includes(apiFileName) || apiFileName.includes(pageFileName) ]; return matches.some(match => match); }); if (newMatchingDiffFile) { console.log(`[温和异步点击] 方法3成功: 找到智能匹配的diff: ${newMatchingDiffFile}`); resolve(fileDiffMap[newMatchingDiffFile]); return; } console.log(`[温和异步点击] 方法3失败: 未找到匹配的diff: ${file.fileName}`); resolve(null); }, 3000); }; setTimeout(() => { if (window.requestIdleCallback) { window.requestIdleCallback(gentleCallback, { timeout: 5000 }); } else { setTimeout(gentleCallback, 500); } }, 1000); }); } console.log(`[温和异步点击] 所有方法都失败: ${file.fileName}`); return null; } catch (error) { console.error(`[温和异步点击] 点击文件 ${file.fileName} 时发生错误:`, error); // 恢复之前的diff缓存 fileDiffMap = { ...previousDiffMap }; return null; } }
最新发布
08-06
<template> <div class="container"> <titlepage :title="pathName" goStr="/dashBoard"></titlepage> <!-- 物业用户 --> <div class="line"> <div>物业用户</div> <a-upload v-if="pathName == '上传文件'" :customRequest="handleUpload" :data="{ type: 1 }" :showUploadList="false" > <a-button size="small" type="primary" class="upload-btn"> <a-icon type="upload" />上传附件 </a-button> </a-upload> </div> <div class="files"> <div class="file-item" v-for="(file, index) in propertyFiles" :key="index"> <div class="file-name" @click="previewFile(file)">{{ file.fileName }}</div> <div class="del-btn" v-if="pathName == '上传文件'" @click="deleteFile(file, 1)">删除</div> </div> </div> <!-- 街镇用户 --> <div class="line"> <div>街镇用户</div> <a-upload v-if="pathName == '上传文件'" :customRequest="handleUpload" :data="{ type: 2 }" :showUploadList="false" > <a-button size="small" type="primary" class="upload-btn"> <a-icon type="upload" />上传附件 </a-button> </a-upload> </div> <div class="files"> <div class="file-item" v-for="(file, index) in streetFiles" :key="index"> <div class="file-name" @click="previewFile(file)">{{ file.fileName }}</div> <div class="del-btn" v-if="pathName == '上传文件'" @click="deleteFile(file, 2)">删除</div> </div> </div> <!-- 房管局用户 --> <div class="line"> <div>房管局用户</div> <a-upload v-if="pathName == '上传文件'" :customRequest="handleUpload" :data="{ type: 3 }" :showUploadList="false" > <a-button size="small" type="primary" class="upload-btn"> <a-icon type="upload" />上传附件 </a-button> </a-upload> </div> <div class="files"> <div class="file-item" v-for="(file, index) in housingFiles" :key="index"> <div class="file-name" @click="previewFile(file)">{{ file.fileName }}</div> <div class="del-btn" v-if="pathName == '上传文件'" @click="deleteFile(file, 3)">删除</div> </div> </div> <!-- 浮动按钮 --> <div class="fixed-btn"> <a-button shape="circle" type="primary" class="round-btn" @click="goToUpload"> {{ pathName == '上传文件' ? '返回' : '上传' }} </a-button> </div> <!-- 预览组件 --> <div class="preview-tools" v-if="showPreview"> <div class="preview-header"> <div @click="showPreview = false"> <a-icon type="close-circle" style="color: #fff;font-size: 32px;" /> </div> </div> <video :src="previewUrl" ref="video" controls autoplay v-if="previewType == 'video'"></video> <audio :src="previewUrl" ref="audio" controls v-if="previewType == 'audio'"></audio> </div> </div> </template> <script> import { postInstructionsFile, getInstructionsList, deleteInstructionsId, deleteInstructionsUrl } from '@/api/instructions'; export default { name: 'Instructions', data() { return { pathName: "操作指南", propertyFiles: [], // 物业用户文件 streetFiles: [], // 街镇用户文件 housingFiles: [], // 房管局用户文件 showPreview: false, // 控制预览工具的显示 previewType: '', // 预览的文件类型 previewUrl: '' // 预览文件URL }; }, mounted() { this.fetchFileList(); }, methods: { // 获取文件列表 fetchFileList() { getInstructionsList().then(res => { if (res.success) { // 修复:正确处理接口返回的数据结构 const result = res.result || {}; // 确保我们处理的是数组 const fileList = Array.isArray(result) ? result : result.data ? result.data : result.list ? result.list : []; // 按类型分类文件 this.propertyFiles = fileList.filter(file => file.type === 1); this.streetFiles = fileList.filter(file => file.type === 2); this.housingFiles = fileList.filter(file => file.type === 3); } else { console.error('获取文件列表失败:', res.message || '未知错误'); } }).catch(error => { console.error('获取文件列表错误:', error); }); }, // 切换页面模式(查看/上传) goToUpload() { this.pathName = this.pathName === "操作指南" ? "上传文件" : "操作指南"; }, // 自定义上传处理(已修复路径问题) handleUpload(data) { console.log(data) const formData = new FormData() formData.append('file', data.file) formData.append('TempleteFile', data.file.name) // 调用上传接口 postInstructionsFile(formData,data.data.type).then(res => { if (res.success) { this.$message.success('上传成功'); this.fetchFileList(); // 刷新文件列表 } else { // 更详细的错误处理 const errorMsg = res.message || '上传失败'; this.$message.error(`上传失败: ${errorMsg}`); // 调试信息 console.error('上传失败详情:', { status: res.code, message: res.message, timestamp: res.timestamp }); } }).catch(error => { console.error('上传请求错误:', error); this.$message.error('上传请求失败'); }); }, // 修改后的上传处理方法 // handleUpload(options) { // const { file, data } = options; // const formData = new FormData(); // formData.append('file', file); // 使用后端要求的字段名'file' // // 调用上传接口 // postInstructionsFile(formData, data.type).then(res => { // if (res.success) { // this.$message.success('上传成功'); // this.fetchFileList(); // 刷新文件列表 // } else { // const errorMsg = res.message || '上传失败'; // this.$message.error(`上传失败: ${errorMsg}`); // console.error('上传失败详情:', res); // } // }).catch(error => { // console.error('上传请求错误:', error); // this.$message.error('上传请求失败'); // }); // }, // 预览文件 previewFile(file) { const extension = file.fileName.split('.').pop().toLowerCase(); if (['mp4', 'avi', 'mov'].includes(extension)) { this.previewType = 'video'; this.previewUrl = file.fileUrl; this.showPreview = true; } else if (['mp3', 'wav'].includes(extension)) { this.previewType = 'audio'; this.previewUrl = file.fileUrl; this.showPreview = true; } else if (extension === 'pdf') { window.open(file.fileUrl, '_blank'); } else { this.$message.warning('不支持预览此文件类型'); } }, // 删除文件 deleteFile(file, type) { const that = this; this.$confirm({ title: '确定要删除这个文件吗?', content: `文件: ${file.fileName}`, onOk() { deleteInstructionsUrl({ url: file.fileUrl }).then(res => { if (res.success) { that.$message.success('删除成功'); // 刷新文件列表 that.fetchFileList(); } else { that.$message.error('删除失败: ' + (res.message || '')); } }).catch(error => { console.error('删除错误:', error); that.$message.error('删除失败'); }); } }); } } }; </script> <style scoped lang="less"> .container { width: 100%; font-size: 16px; .line { display: flex; align-items: center; padding: 10px 20px; font-weight: bold; border-bottom: 1px solid #eee; .upload-btn { margin-left: 20px; font-weight: normal; } } .files { padding: 10px 20px; font-size: 14px; .file-item { display: flex; align-items: center; padding: 8px 0; border-bottom: 1px dashed #eee; .file-name { text-decoration: underline; cursor: pointer; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .del-btn { color: #ff4d4f; cursor: pointer; margin-left: 10px; text-decoration: underline; font-size: 12px; } } } .fixed-btn { position: fixed; bottom: 80px; right: 20px; .round-btn { width: 50px; height: 50px; font-size: 12px; line-height: 50px; text-align: center; } } .preview-tools { width: 100vw; height: 100vh; position: fixed; left: 0; top: 0; z-index: 200; background: rgba(0, 0, 0, 0.9); display: flex; justify-content: center; align-items: center; .preview-header { position: absolute; top: 20px; right: 20px; z-index: 201; } video, audio { max-width: 90vw; max-height: 90vh; outline: none; } } } </style> 文件上传成功,页面为啥没有显示
07-18
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值