二期:自动同步功能开发

This commit is contained in:
朱浩 2024-07-23 13:43:41 +08:00
parent 8ed27fc925
commit e1fc883870
6 changed files with 262 additions and 75 deletions

View File

@ -30,6 +30,7 @@
"jsencrypt": "^3.3.2", "jsencrypt": "^3.3.2",
"pinia": "^2.1.7", "pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.1", "pinia-plugin-persistedstate": "^3.2.1",
"spark-md5": "^3.0.2",
"vue-cropper": "^1.0.3", "vue-cropper": "^1.0.3",
"vue-router": "^4.4.0" "vue-router": "^4.4.0"
}, },

View File

@ -1,16 +1,105 @@
import CryptoJS from 'crypto-js' import SparkMD5 from 'spark-md5'
const fs = require('fs') const fs = require('fs')
const path = require('path') const path = require('path')
import { ElectronDownloadManager } from 'electron-dl-manager' import { ElectronDownloadManager } from 'electron-dl-manager'
import { dialog } from 'electron' import { dialog } from 'electron'
import axios from 'axios' import axios from 'axios'
const uploadUrl = import.meta.env.VITE_APP_UPLOAD_API + '/smarttalk/file/upload' const uploadUrl = import.meta.env.VITE_APP_UPLOAD_API + '/smarttalk/file/upload'
const asyncUploadUrl = import.meta.env.VITE_APP_UPLOAD_API + '/smarttalk/file/asyncUpload'
const manager = new ElectronDownloadManager() const manager = new ElectronDownloadManager()
export default async function ({ app, shell, BrowserWindow, ipcMain }) { export default async function ({ app, shell, BrowserWindow, ipcMain }) {
const userDataPath = app.getPath('userData') const userDataPath = app.getPath('userData')
const appRootFilePath = userDataPath + '\\selfFile\\' const appRootFilePath = userDataPath + '\\selfFile\\'
const appTempFilePath = userDataPath + '\\tempFile\\' const appTempFilePath = userDataPath + '\\tempFile\\'
let Spark = new SparkMD5.ArrayBuffer()
ipcMain.on('upload-file-change', (e, { id, fileNewName, cookie, fileType }) => {
let filePath = appRootFilePath + fileNewName
//执行更新,上传文件
let formData = new FormData()
formData.append('id', id)
uploadFileByFS({
url: asyncUploadUrl,
path: filePath,
name: fileNewName,
cookie,
fileType,
formData,
success: (response) => {
e.reply('upload-file-change-success' + fileNewName, {
data: response.data,
md5: formData.md5
})
},
error: (err) => {
console.error('Error uploading file:', err)
}
})
})
/*监听文件改变,如果有改变则返回触发*/
ipcMain.on('listen-file-change', (e, { id, fileNewName, md5, cookie, fileType }) => {
let filePath = appRootFilePath + fileNewName
let uploadId = null
let isOn = false
setInterval(() => {
getFileMD5(filePath).then((md5New) => {
if (md5New !== md5) {
md5 = md5New
if (uploadId) {
clearTimeout(uploadId)
}
if (isOn === false) {
e.reply('listen-file-change-on' + fileNewName)
isOn = true
}
//倒数十秒提交更改,十秒之内有继续修改则重置倒数
uploadId = setTimeout(() => {
//执行更新,上传文件
let formData = new FormData()
formData.append('id', id)
uploadFileByFS({
url: asyncUploadUrl,
path: filePath,
name: fileNewName,
cookie,
fileType,
formData,
success: (response) => {
e.reply('listen-file-change-success' + fileNewName, {
data: response.data,
md5: formData.md5
})
clearTimeout(uploadId)
isOn = false
},
error: (err) => {
console.error('Error uploading file:', err)
}
})
}, 5000)
}
})
}, 1000)
})
function getFileMD5(path) {
return new Promise((resolve, reject) => {
fs.readFile(path, (err, dataFile) => {
if (err) {
reject(err)
return console.error(err)
}
Spark.append(dataFile)
let md5 = Spark.end()
resolve(md5)
})
})
}
/*
* 判断是否有本地文件
* */
ipcMain.on('is-have-local-file', (e, fileNewName) => { ipcMain.on('is-have-local-file', (e, fileNewName) => {
let filePath = appRootFilePath + fileNewName let filePath = appRootFilePath + fileNewName
fs.access(filePath, fs.constants.F_OK, (err) => { fs.access(filePath, fs.constants.F_OK, (err) => {
@ -21,10 +110,38 @@ export default async function ({ app, shell, BrowserWindow, ipcMain }) {
e.reply('is-have-local-file-reply' + fileNewName, true) e.reply('is-have-local-file-reply' + fileNewName, true)
}) })
}) })
/*
* 判断是需要同步本地文件
* */
ipcMain.on('is-async-local-file', (e, { fileNewName, lastModifyTime, md5 }) => {
let filePath = appRootFilePath + fileNewName
fs.access(filePath, fs.constants.F_OK, (err) => {
if (err) {
e.reply('is-async-local-file-reply' + fileNewName, { isAsync: true, type: 'down' })
return
}
getFileMD5(filePath).then((localMd5) => {
if (localMd5 === md5) {
e.reply('is-async-local-file-reply' + fileNewName, { isAsync: false, type: '' })
} else {
const stats = fs.statSync(filePath)
//如果线上时间大于线下时间,就需要从线上下载,否则则需要上传
if (lastModifyTime > stats.mtime.getTime()) {
e.reply('is-async-local-file-reply' + fileNewName, { isAsync: true, type: 'down' })
} else if (lastModifyTime < stats.mtime.getTime()) {
e.reply('is-async-local-file-reply' + fileNewName, { isAsync: true, type: 'upload' })
}
}
})
})
})
//默认浏览器打开url //默认浏览器打开url
ipcMain.on('open-url-browser', (e, url) => { ipcMain.on('open-url-browser', (e, url) => {
shell.openPath(url) shell.openPath(url)
}) })
//使用默认应用打开本地文件 //使用默认应用打开本地文件
ipcMain.on('open-path-app', (e, destination) => { ipcMain.on('open-path-app', (e, destination) => {
let path = appRootFilePath + destination let path = appRootFilePath + destination
@ -40,65 +157,72 @@ export default async function ({ app, shell, BrowserWindow, ipcMain }) {
}) })
}) })
//复制文件 //导出文件
ipcMain.on('export-file-default', (e, list) => { ipcMain.on('export-file-default', (e, list) => {
exportFile(list, (res) => { exportFile(list, (res) => {
e.reply('export-file-default-reply', res) e.reply('export-file-default-reply', res)
}) })
}) })
function getFileMD5(file) { function uploadFileByFS({ url, path, name, cookie, fileType, formData, success, error }) {
return new Promise((resolve, reject) => { fs.readFile(path, (err, data) => {
const fileReader = new FileReader() if (err) {
fileReader.onload = (e) => { return console.error(err)
const buffer = e.target.result
let md5 = CryptoJS.MD5(buffer).toString()
resolve(md5)
} }
fileReader.readAsArrayBuffer(file) // 配置上传的请求
const config = {
headers: {
'Content-Type': 'multipart/form-data', // 或者其他适合上传文件的Content-Type
Authorization: 'Bearer ' + cookie
}
}
Spark.append(data)
let md5 = Spark.end()
// 使用axios上传文件
let file = new File([data], name, {
type: fileType
})
formData.append('file', file)
formData.append('md5', md5)
axios
.post(url, formData, config)
.then((response) => {
success(response)
})
.catch((errorMsg) => {
error(errorMsg)
})
}) })
} }
/*创建新的ppt文件*/
ipcMain.on('creat-file-default', (e, { name, uploadData, cookie }) => { ipcMain.on('creat-file-default', (e, { name, uploadData, cookie }) => {
createFolder('tempFile').then(() => { createFolder('tempFile').then(() => {
let path = appTempFilePath + name let path = appTempFilePath + name
fs.writeFileSync(path, '', 'utf-8') fs.writeFileSync(path, '', 'utf-8')
// 读取文件 let fileType = 'application/vnd.openxmlformats-officedocument.presentationml.presentation'
fs.readFile(path, (err, data) => { let formData = new FormData()
if (err) { for (let key in uploadData) {
return console.error(err) if (Object.prototype.hasOwnProperty.call(uploadData, key)) {
// 检查是否是对象自身的属性
formData.append(key, uploadData[key])
} }
// 配置上传的请求 }
const config = { formData.append('fileFlag', '教案')
headers: { uploadFileByFS({
'Content-Type': 'multipart/form-data', // 或者其他适合上传文件的Content-Type url: uploadUrl,
Authorization: 'Bearer ' + cookie path,
} name,
cookie,
fileType,
formData,
success: (response) => {
e.reply('creat-file-default-reply', response.data)
console.log('File uploaded successfully:', response.data)
},
error: (err) => {
console.error('Error uploading file:', err)
} }
let md5 = CryptoJS.MD5(data).toString()
let formData = new FormData()
// 使用axios上传文件
let file = new File([data], name, {
type: 'application/vnd.openxmlformats-officedocument.presentationml.presentation'
})
formData.append('file', file)
formData.append('md5',md5)
for (let key in uploadData) {
if (uploadData.hasOwnProperty(key)) { // 检查是否是对象自身的属性
formData.append(key,uploadData[key])
}
}
formData.append("fileFlag","教案")
axios
.post(uploadUrl, formData, config)
.then((response) => {
e.reply('creat-file-default-reply', response.data)
console.log('File uploaded successfully:', response.data)
})
.catch((error) => {
console.error('Error uploading file:', error)
})
}) })
}) })
}) })
@ -191,6 +315,7 @@ export default async function ({ app, shell, BrowserWindow, ipcMain }) {
}) })
}) })
/*导出文件*/
function exportFile(list, callback) { function exportFile(list, callback) {
let win = BrowserWindow.getFocusedWindow() let win = BrowserWindow.getFocusedWindow()
//通过扩展名识别文件类型 //通过扩展名识别文件类型
@ -220,10 +345,12 @@ export default async function ({ app, shell, BrowserWindow, ipcMain }) {
}) })
} }
/*文件是否已经存在*/
function isHaveFile(path) { function isHaveFile(path) {
return fs.existsSync(path) return fs.existsSync(path)
} }
/*判断是否已经存在这个名字的文件,如果已经存在则递增导出*/
function filterCopyFile(path, index = 0) { function filterCopyFile(path, index = 0) {
if (isHaveFile(path) === true) { if (isHaveFile(path) === true) {
index++ index++
@ -234,6 +361,7 @@ export default async function ({ app, shell, BrowserWindow, ipcMain }) {
} }
} }
/*复制文件*/
function copyRelFile(source, destination, callback) { function copyRelFile(source, destination, callback) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const readStream = fs.createReadStream(source) const readStream = fs.createReadStream(source)
@ -256,6 +384,7 @@ export default async function ({ app, shell, BrowserWindow, ipcMain }) {
}) })
} }
/*复制文件*/
function copyFile(source, destination, callback) { function copyFile(source, destination, callback) {
let path = appRootFilePath + destination let path = appRootFilePath + destination
createFolder('selfFile').then(() => { createFolder('selfFile').then(() => {
@ -276,6 +405,7 @@ export default async function ({ app, shell, BrowserWindow, ipcMain }) {
}) })
} }
/*创建文件夹*/
function createFolder(folderName) { function createFolder(folderName) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const folderPath = path.join(userDataPath, folderName) const folderPath = path.join(userDataPath, folderName)

View File

@ -59,7 +59,8 @@
<script> <script>
import uploaderState from '@/store/modules/uploader' import uploaderState from '@/store/modules/uploader'
import { getToken } from '@/utils/auth' import { getToken } from '@/utils/auth'
import CryptoJS from 'crypto-js' // import CryptoJS from 'crypto-js/md5'
import SparkMD5 from 'spark-md5'
import { DeleteFilled } from '@element-plus/icons-vue' import { DeleteFilled } from '@element-plus/icons-vue'
const { ipcRenderer } = window.electron || {} const { ipcRenderer } = window.electron || {}
export default { export default {
@ -147,13 +148,14 @@ export default {
}, },
getFileMD5(file) { getFileMD5(file) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const fileReader = new FileReader() let fileReader = new FileReader()
fileReader.onload = (e) => { let Spark = new SparkMD5.ArrayBuffer()
const buffer = e.target.result fileReader.readAsArrayBuffer(file)
let md5 = CryptoJS.MD5(buffer).toString() fileReader.onload = function (e) {
Spark.append(e.target.result)
let md5 = Spark.end()
resolve(md5) resolve(md5)
} }
fileReader.readAsArrayBuffer(file)
}) })
}, },
removeUploadFile(uid) { removeUploadFile(uid) {

View File

@ -9,6 +9,15 @@ export const isHaveLocalFile = async (fileNewName) => {
}) })
} }
export const isAsyncLocalFile = async (fileNewName, lastModifyTime, md5) => {
return new Promise((resolve, reject) => {
ipcRenderer.send('is-async-local-file', { fileNewName, lastModifyTime, md5 })
ipcRenderer.once('is-async-local-file-reply' + fileNewName, (e, { isAsync, type }) => {
resolve({ isAsync, type })
})
})
}
export const parseCataByNode = (node) => { export const parseCataByNode = (node) => {
if (node.parentNode) { if (node.parentNode) {
let arr = parseCataByNode(node.parentNode) let arr = parseCataByNode(node.parentNode)
@ -31,9 +40,12 @@ export const exportFile = async (list) => {
export const creatPPT = (name, uploadData) => { export const creatPPT = (name, uploadData) => {
JSON.parse(JSON.stringify(uploadData)) JSON.parse(JSON.stringify(uploadData))
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let cookie = localStorage.getItem('Admin-Token'); let cookie = localStorage.getItem('Admin-Token')
console.log(cookie) ipcRenderer.send('creat-file-default', {
ipcRenderer.send('creat-file-default', { name, uploadData:JSON.parse(JSON.stringify(uploadData)), cookie }) name,
uploadData: JSON.parse(JSON.stringify(uploadData)),
cookie
})
ipcRenderer.once('creat-file-default-reply', (e, res) => { ipcRenderer.once('creat-file-default-reply', (e, res) => {
resolve(res) resolve(res)
}) })

View File

@ -112,18 +112,23 @@ export default {
props: { props: {
item: { item: {
type: Object, type: Object,
default: function() { default: function () {
return {} return {}
} }
}, },
index: { index: {
type: Number, type: Number,
default: function() { default: function () {
return 0 return 0
} }
} }
}, },
emits: { 'on-move': null, 'on-delete': null }, emits: { 'on-move': null, 'on-delete': null },
data() {
return {
listenList: []
}
},
methods: { methods: {
editTalk(item) { editTalk(item) {
ElMessageBox.prompt('请输入新的名称', '重命名', { ElMessageBox.prompt('请输入新的名称', '重命名', {
@ -142,8 +147,7 @@ export default {
} }
}) })
}) })
.catch(() => { .catch(() => {})
})
}, },
downloadFile(item) { downloadFile(item) {
ipcRenderer.send('save-as', item.fileFullPath, item.fileShowName) ipcRenderer.send('save-as', item.fileFullPath, item.fileShowName)
@ -156,7 +160,6 @@ export default {
}) })
}, },
closePopver(index) { closePopver(index) {
console.log(this.$refs['popover_' + index])
this.$refs['popover_' + index].hide() this.$refs['popover_' + index].hide()
}, },
moveSmarttalkFun(item) { moveSmarttalkFun(item) {
@ -183,6 +186,25 @@ export default {
isHaveLocalFile(item.fileNewName).then((res) => { isHaveLocalFile(item.fileNewName).then((res) => {
if (res === true) { if (res === true) {
ipcRenderer.send('open-path-app', item.fileNewName) ipcRenderer.send('open-path-app', item.fileNewName)
if (this.listenList.indexOf(item.fileNewName) === -1) {
this.listenList.push(item.fileNewName)
let cookie = localStorage.getItem('Admin-Token')
ipcRenderer.send('listen-file-change', {
id: item.id,
fileNewName: item.fileNewName,
md5: item.fileMd5,
cookie,
fileType: item.fileType
})
ipcRenderer.on('listen-file-change-on' + item.fileNewName, () => {
item.async = 'on'
})
ipcRenderer.on('listen-file-change-success' + item.fileNewName, (e,{data,md5}) => {
item.fileSize = data.fileSize
item.md5 = md5
item.async = true
})
}
} else { } else {
item.async = 'on' item.async = 'on'
ipcRenderer.once('download-file-default' + item.fileNewName, (e, isSuccess) => { ipcRenderer.once('download-file-default' + item.fileNewName, (e, isSuccess) => {

View File

@ -79,7 +79,7 @@ import FileListItem from '@/views/prepare/container/file-list-item.vue'
import { getSmarttalkPage, moveSmarttalk } from '@/api/file' import { getSmarttalkPage, moveSmarttalk } from '@/api/file'
import { toTimeText } from '@/utils/date' import { toTimeText } from '@/utils/date'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { isHaveLocalFile, parseCataByNode, creatPPT } from '@/utils/talkFile' import { isHaveLocalFile, parseCataByNode, creatPPT, isAsyncLocalFile } from '@/utils/talkFile'
import FileOperBatch from '@/views/prepare/container/file-oper-batch.vue' import FileOperBatch from '@/views/prepare/container/file-oper-batch.vue'
const { ipcRenderer } = window.electron || {} const { ipcRenderer } = window.electron || {}
export default { export default {
@ -134,7 +134,7 @@ export default {
}, },
methods: { methods: {
createFile() { createFile() {
creatPPT('新建ppt文档.pptx',this.uploadData).then((res) => { creatPPT(this.currentNode.label + '.pptx', this.uploadData).then((res) => {
this.currentFileList.unshift(res.resData) this.currentFileList.unshift(res.resData)
}) })
}, },
@ -166,22 +166,42 @@ export default {
this.asyncAllFileVisiable = true this.asyncAllFileVisiable = true
const test = (item) => { const test = (item) => {
return new Promise((resolve) => { return new Promise((resolve) => {
isHaveLocalFile(item.fileNewName).then((res) => { //线
item.async = res isAsyncLocalFile(item.fileNewName, item.lastModifyTime, item.fileMd5).then(
if (res === false) { ({ isAsync, type }) => {
ipcRenderer.send('download-file-default', { item.async = !isAsync
url: item.fileFullPath, if (isAsync === true) {
fileName: item.fileNewName item.async = 'on'
}) if (type === 'down') {
item.async = 'on' ipcRenderer.send('download-file-default', {
ipcRenderer.once('download-file-default' + item.fileNewName, (e, isSuccess) => { url: item.fileFullPath,
item.async = isSuccess fileName: item.fileNewName
})
ipcRenderer.once('download-file-default' + item.fileNewName, (e, isSuccess) => {
item.async = isSuccess
resolve()
})
}
if (type === 'upload') {
ipcRenderer.send('upload-file-change', {
url: item.fileFullPath,
fileName: item.fileNewName
})
ipcRenderer.once(
'upload-file-change-success' + item.fileNewName,
(e, { data, md5 }) => {
item.fileSize = data.fileSize
item.md5 = md5
item.async = true
resolve()
}
)
}
} else {
resolve() resolve()
}) }
} else {
resolve()
} }
}) )
}) })
} }
for (let i = 0; i < this.currentFileList.length; i++) { for (let i = 0; i < this.currentFileList.length; i++) {