// 注册 Vant 组件 Vue.use(vant.NavBar); Vue.use(vant.Icon); Vue.use(vant.Tag); Vue.use(vant.Loading); Vue.use(vant.Notify); Vue.use(vant.Dialog); Vue.use(vant.Toast); Vue.use(vant.Collapse); Vue.use(vant.CollapseItem); Vue.use(vant.Field); Vue.use(vant.Form); Vue.use(vant.Button); Vue.use(vant.Overlay); var app = new Vue({ el: "#app", data: { // 同步状态 isSyncing: false, syncButtonText: '开始同步', // 进度相关 showProgress: false, progressPercent: 0, progressText: '准备同步', // 统计数据 showStats: false, totalTasks: 0, successCount: 0, errorCount: 0, runningCount: 0, // 上传任务列表 uploadTasks: [ { id: 1, taskName: '盘点数据上传', status: 'pending' }, // { id: 2, taskName: '资产数据上传', status: 'pending' }, ], // 下载任务列表 downloadTasks: [ { id: 1, taskName: '盘点数据下载', status: 'pending' }, // { id: 2, taskName: '资产数据下载', status: 'pending' }, ], // 折叠面板激活的名称 activeNames: ['upload', 'download'], // IndexDB 数据库实例 checkVouchDb: null, // 登录相关 loginShow: false, username: null, password: null, clientId: null, // 表单验证 usernameError: false, usernameErrorMsg: '', passwordError: false, passwordErrorMsg: '', }, methods: { /** * 返回上一页 */ goBack: function () { window.location.href = './Menu.html'; }, /** * 开始同步 */ startSync: function () { var _self = this; // 显示确认对话框 vant.Dialog.confirm({ title: '确认同步', message: '确定要开始同步数据吗?', }).then(function () { // 确认后先判断登录状态 _self.judgementLogin(); }).catch(function () { // 用户取消 }); }, /** * 判断是否登录 * 1.判断网络是否连接 * 2.判断token是否为null * 3.判断token是否正确 */ judgementLogin: function () { var _self = this; $.ajax({ url: Common.getApi('/authApi/LoginResource/getCurrentDate'), type: 'get', dataType: "text", success: function (data) { var token = localStorage.getItem("token"); if (token != null) { $.ajax({ url: Common.getApi('/api/userResource/authenticationToken'), type: 'get', data: { token: token }, success: function (data) { if (data.errorCode != 0) { _self.loginShow = true; } else { // token有效,直接执行同步 _self.executeSync(); } }, error: function (e) { }, }); } else { _self.loginShow = true; } }, error: function (e) { if (e.readyState == 0) { vant.Dialog.alert({ title: '错误', message: '网络未连接无法进行同步', theme: 'round-button', }); } }, }); }, /** * 登录 */ login: function () { var _self = this; var username = _self.username; var password = _self.password; var loginData = { userName: username, password: password, accountDateTime: '', languageId: 'zh-CN' }; $.ajax({ url: Common.getApi('/authApi/LoginResource/login'), type: 'post', data: loginData, contentType: 'application/x-www-form-urlencoded; charset=UTF-8', success: function (loginInfo) { var json_LoginInfo = JSON.stringify(loginInfo); localStorage.setItem('json_LoginInfo', json_LoginInfo); localStorage.setItem('token', loginInfo.data.token); localStorage.setItem('account', loginInfo.data.accountId); localStorage.setItem('clientId', loginInfo.data.loginClientId); $.ajax({ type: "post", url: Common.getApi('/api/TokenClientResource/saveTokenClient'), contentType: "application/json", beforeSend: function (request) { Common.addTokenToRequest(request); }, success: function (data) { _self.loginShow = false; vant.Notify({ type: 'success', message: '登录成功', duration: 1000, }); _self.clientId = loginInfo.data.loginClientId; // 登录成功后执行同步 _self.executeSync(); }, error: function (e) { Common.processExceptionVant(e); } }); }, error: function (e) { vant.Notify({ type: 'danger', message: '用户名或密码错误', duration: 1000, }); }, }); }, /** * 取消登录 */ onCancelLogin: function () { var _self = this; _self.loginShow = false; _self.username = null; _self.password = null; _self.resetFormErrors(); }, /** * 验证用户名 */ validateUsername: function () { var _self = this; if (!_self.username || _self.username.trim() === '') { _self.usernameError = true; _self.usernameErrorMsg = '请输入用户名'; return false; } else { _self.usernameError = false; _self.usernameErrorMsg = ''; return true; } }, /** * 验证密码 */ validatePassword: function () { var _self = this; if (!_self.password || _self.password.trim() === '') { _self.passwordError = true; _self.passwordErrorMsg = '请输入密码'; return false; } else { _self.passwordError = false; _self.passwordErrorMsg = ''; return true; } }, /** * 重置表单错误状态 */ resetFormErrors: function () { var _self = this; _self.usernameError = false; _self.usernameErrorMsg = ''; _self.passwordError = false; _self.passwordErrorMsg = ''; }, /** * 处理登录表单提交 */ handleLogin: function () { var _self = this; // 验证表单 var usernameValid = _self.validateUsername(); var passwordValid = _self.validatePassword(); if (usernameValid && passwordValid) { // 表单验证通过,执行登录 _self.login(); } }, /** * 执行同步流程 */ executeSync: function () { var _self = this; // 重置状态 _self.resetState(); // 开始同步 _self.isSyncing = true; _self.syncButtonText = '同步中...'; _self.showProgress = true; _self.showStats = true; // 计算总任务数 _self.totalTasks = _self.uploadTasks.length + _self.downloadTasks.length; vant.Toast.loading({ message: '同步开始...', forbidClick: true, duration: 1500, }); // 延迟开始,显示加载提示 setTimeout(function () { _self.startUploadTasks(); }, 1000); }, /** * 重置状态 */ resetState: function () { var _self = this; // 重置进度 _self.progressPercent = 0; _self.progressText = '准备同步'; // 重置统计 _self.successCount = 0; _self.errorCount = 0; _self.runningCount = 0; // 重置上传任务状态 _self.uploadTasks.forEach(function (task) { task.status = 'pending'; }); // 重置下载任务状态 _self.downloadTasks.forEach(function (task) { task.status = 'pending'; }); }, /** * 开始上传任务 */ startUploadTasks: function () { var _self = this; _self.progressText = '开始上传数据'; // 顺序执行上传任务 _self.executeTasksSequentially(_self.uploadTasks, '上传', function () { // 上传完成后,开始下载 _self.startDownloadTasks(); }); }, /** * 开始下载任务 */ startDownloadTasks: function () { var _self = this; _self.progressText = '开始下载数据'; // 顺序执行下载任务 _self.executeTasksSequentially(_self.downloadTasks, '下载', function () { // 全部完成 _self.onSyncComplete(); }); }, /** * 顺序执行任务列表 * @param {Array} tasks - 任务列表 * @param {String} type - 任务类型(上传/下载) * @param {Function} callback - 完成回调 */ executeTasksSequentially: function (tasks, type, callback) { var _self = this; var currentIndex = 0; function executeNext() { if (currentIndex >= tasks.length) { // 所有任务完成 callback(); return; } var task = tasks[currentIndex]; _self.executeTask(task, type, function () { currentIndex++; executeNext(); }); } executeNext(); }, /** * 执行单个任务 * @param {Object} task - 任务对象 * @param {String} type - 任务类型 * @param {Function} callback - 完成回调 */ executeTask: function (task, type, callback) { var _self = this; // 更新状态为运行中 task.status = 'running'; _self.runningCount++; _self.progressText = type + '中: ' + task.taskName; // 根据任务名称选择对应的 API 调用 var apiPromise; if (task.taskName === '盘点数据上传') { apiPromise = _self.uploadInventoryData(); } else if (task.taskName === '盘点数据下载') { apiPromise = _self.downloadInventoryData(); } else { // 未知任务类型,直接失败 apiPromise = Promise.reject('未知的任务类型'); } // 执行API调用 apiPromise.then(function (result) { // 成功 task.status = 'success'; _self.successCount++; _self.runningCount--; // 更新进度 _self.updateProgress(); var message = task.taskName + ' 成功'; // if (result.count !== undefined) { // message += ' (' + result.count + '条)'; // } vant.Notify({ type: 'success', message: message, duration: 1000, }); // 延迟继续下一个任务 setTimeout(callback, 500); }).catch(function (error) { // 失败 task.status = 'error'; _self.errorCount++; _self.runningCount--; // 更新进度 _self.updateProgress(); vant.Notify({ type: 'danger', message: task.taskName + ' 失败: ' + error, duration: 1500, }); // 延迟继续下一个任务(即使失败也继续) setTimeout(callback, 500); }); }, /** * 清除所有 IndexDB 数据 * @returns {Promise} */ clearAllIndexDBData: function () { var _self = this; return new Promise(function (resolve, reject) { // 清除 uploadQueue 的所有数据 _self.checkVouchDb.getAll('uploadQueue', function (uploadQueueData) { var deleteUploadPromises = []; if (uploadQueueData && uploadQueueData.length > 0) { uploadQueueData.forEach(function (item) { deleteUploadPromises.push(new Promise(function (res, rej) { _self.checkVouchDb.deleteOne('uploadQueue', item.checkVouchId, function () { res(); }, function (error) { rej(error); }); })); }); } Promise.all(deleteUploadPromises).then(function () { console.log('uploadQueue 数据已清除'); // 清除 checkVouchList 的所有数据 _self.checkVouchDb.getAll('checkVouchList', function (checkVouchListData) { var deleteCheckPromises = []; if (checkVouchListData && checkVouchListData.length > 0) { checkVouchListData.forEach(function (item) { deleteCheckPromises.push(new Promise(function (res, rej) { _self.checkVouchDb.deleteOne('checkVouchList', item.checkVouchId, function () { res(); }, function (error) { rej(error); }); })); }); } Promise.all(deleteCheckPromises).then(function () { console.log('checkVouchList 数据已清除'); resolve(); }).catch(function (error) { reject('清除 checkVouchList 失败: ' + error); }); }, function (error) { reject('获取 checkVouchList 失败: ' + error); }); }).catch(function (error) { reject('清除 uploadQueue 失败: ' + error); }); }, function (error) { // 如果获取失败,尝试继续清除 checkVouchList console.warn('获取 uploadQueue 失败,跳过:', error); _self.checkVouchDb.getAll('checkVouchList', function (checkVouchListData) { var deleteCheckPromises = []; if (checkVouchListData && checkVouchListData.length > 0) { checkVouchListData.forEach(function (item) { deleteCheckPromises.push(new Promise(function (res, rej) { _self.checkVouchDb.deleteOne('checkVouchList', item.checkVouchId, function () { res(); }, function (error) { rej(error); }); })); }); } Promise.all(deleteCheckPromises).then(function () { console.log('checkVouchList 数据已清除'); resolve(); }).catch(function (error) { reject('清除 checkVouchList 失败: ' + error); }); }, function (error) { reject('获取 checkVouchList 失败: ' + error); }); }); }); }, /** * 执行API调用 - 下载盘点数据 * @returns {Promise} */ downloadInventoryData: function () { var _self = this; return new Promise(function (resolve, reject) { // 下载前先清空所有 IndexDB 数据 console.log('下载前清空所有 IndexDB 数据'); _self.clearAllIndexDBData().then(function () { console.log('IndexDB 已清空,开始下载数据'); // 执行下载 $.ajax({ url: Common.getApi('/api/CheckVouchResource/inventoryDataDownload'), type: 'post', contentType: "application/json", beforeSend: function (request) { Common.addTokenToRequest(request); }, success: function (response) { if (response.errorCode === 0 && response.datas) { // 存储盘点单数据到 IndexDB(完整保存,包含 checkVouchDate 信息) var savePromises = []; response.datas.forEach(function (checkVouch) { savePromises.push(new Promise(function (res, rej) { // 直接保存完整的盘点单数据到 checkVouchList // 在 AssetInventoryCheck.js 加载时,会根据 checkVouchDate 区分已盘和待盘 _self.checkVouchDb.put('checkVouchList', checkVouch, function () { var totalItems = checkVouch.cfCheckVouchsResponses ? checkVouch.cfCheckVouchsResponses.length : 0; console.log('盘点单保存成功:', checkVouch.checkVouchId, ',共', totalItems, '条数据'); res(); }, function (error) { console.error('盘点单保存失败:', error); rej(error); }); })); }); // 等待所有盘点单保存完成 Promise.all(savePromises).then(function () { resolve({ errorCode: 0, message: '盘点数据下载成功', count: response.datas.length }); }).catch(function (error) { reject('保存盘点数据失败: ' + error); }); } else { reject(response.errorMessage || '下载盘点数据失败'); } }, error: function (xhr, status, error) { console.error('下载盘点数据失败:', error); reject('网络错误: ' + error); } }); }).catch(function (error) { // 清空 IndexDB 失败 console.error('清空 IndexDB 失败:', error); reject('清空 IndexDB 失败: ' + error); }); }); }, /** * 执行API调用 - 上传盘点数据 * @returns {Promise} */ uploadInventoryData: function () { var _self = this; return new Promise(function (resolve, reject) { // 从 IndexDB 读取待上传的盘点数据 _self.checkVouchDb.getAll('uploadQueue', function (uploadDataList) { // 如果没有数据,使用空数组 var dataToUpload = uploadDataList || []; var hasData = dataToUpload.length > 0; console.log('准备上传盘点数据,数据量:', dataToUpload.length); // 上传数据 - 数据结构为数组,无论有没有数据都要上传 $.ajax({ url: Common.getApi('/api/CheckVouchResource/inventoryDataUpload'), type: 'POST', contentType: 'application/json', data: JSON.stringify(dataToUpload), beforeSend: function (request) { Common.addTokenToRequest(request); }, success: function (response) { if (response.errorCode === 0) { // 上传成功后,清除所有 IndexDB 数据 console.log('上传成功,开始清除所有 IndexDB 数据'); // 统计总的盘点条数 var totalCount = 0; if (hasData) { dataToUpload.forEach(function (item) { if (item.cfCheckVouchsRequests) { totalCount += item.cfCheckVouchsRequests.length; } }); } // 清除所有数据 _self.clearAllIndexDBData().then(function () { console.log('所有 IndexDB 数据已清除,准备重新下载'); // 清除成功后,自动触发下载 return _self.downloadInventoryData(); }).then(function (downloadResult) { console.log('重新下载完成:', downloadResult); resolve({ errorCode: 0, message: '盘点数据上传成功,已重新下载最新数据', count: totalCount }); }).catch(function (error) { console.error('清除或重新下载失败:', error); // 即使清除或下载失败,上传已成功 resolve({ errorCode: 0, message: '盘点数据上传成功(但重新下载失败: ' + error + ')', count: totalCount }); }); } else { reject(response.errorMessage || '上传盘点数据失败'); } }, error: function (xhr, status, error) { console.error('上传盘点数据失败:', error); reject('网络错误: ' + error); } }); }, function (error) { // 如果读取失败,也尝试上传空数组 console.error('读取待上传数据失败,将上传空数组:', error); $.ajax({ url: Common.getApi('/api/CheckVouchResource/inventoryDataUpload'), type: 'POST', contentType: 'application/json', data: JSON.stringify([]), beforeSend: function (request) { Common.addTokenToRequest(request); }, success: function (response) { if (response.errorCode === 0) { resolve({ errorCode: 0, message: '没有需要上传的盘点数据', count: 0 }); } else { reject(response.errorMessage || '上传盘点数据失败'); } }, error: function (xhr, status, error) { console.error('上传盘点数据失败:', error); reject('网络错误: ' + error); } }); }); }); }, /** * 更新进度 */ updateProgress: function () { var _self = this; // 使用calculateStats来自动计算所有统计数据 _self.calculateStats(); }, /** * 同步完成 */ onSyncComplete: function () { var _self = this; _self.isSyncing = false; _self.progressPercent = 100; _self.progressText = '同步完成'; _self.syncButtonText = '同步完成'; // 显示完成消息 var message = '同步完成!成功: ' + _self.successCount + ' 个,失败: ' + _self.errorCount + ' 个'; if (_self.errorCount === 0) { vant.Dialog.alert({ title: '同步成功', message: '所有数据已成功同步!', theme: 'round-button', }).then(function () { _self.resetButtonText(); }); } else { vant.Dialog.alert({ title: '同步完成', message: message, theme: 'round-button', }).then(function () { _self.resetButtonText(); }); } }, /** * 重置按钮文本 */ resetButtonText: function () { var _self = this; setTimeout(function () { _self.syncButtonText = '开始同步'; }, 1000); }, /** * 添加上传任务(支持动态添加) * @param {String} taskName - 任务名称 */ addUploadTask: function (taskName) { var _self = this; var newId = _self.uploadTasks.length > 0 ? Math.max.apply(Math, _self.uploadTasks.map(function (t) { return t.id; })) + 1 : 1; _self.uploadTasks.push({ id: newId, taskName: taskName, status: 'pending' }); }, /** * 添加下载任务(支持动态添加) * @param {String} taskName - 任务名称 */ addDownloadTask: function (taskName) { var _self = this; var newId = _self.downloadTasks.length > 0 ? Math.max.apply(Math, _self.downloadTasks.map(function (t) { return t.id; })) + 1 : 1; _self.downloadTasks.push({ id: newId, taskName: taskName, status: 'pending' }); }, /** * 清空所有任务 */ clearAllTasks: function () { var _self = this; _self.uploadTasks = []; _self.downloadTasks = []; _self.resetState(); }, /** * 计算统计数据 */ calculateStats: function () { var _self = this; var allTasks = _self.uploadTasks.concat(_self.downloadTasks); _self.totalTasks = allTasks.length; _self.successCount = allTasks.filter(function (t) { return t.status === 'success'; }).length; _self.errorCount = allTasks.filter(function (t) { return t.status === 'error'; }).length; _self.runningCount = allTasks.filter(function (t) { return t.status === 'running'; }).length; // 只有在同步开始后才显示统计卡片 // 初始状态(所有任务都是pending)时不显示 if (_self.successCount > 0 || _self.errorCount > 0 || _self.runningCount > 0) { _self.showStats = true; _self.showProgress = true; _self.progressPercent = Math.round(((_self.successCount + _self.errorCount) / _self.totalTasks) * 100); if (_self.progressPercent === 100) { _self.progressText = '同步完成'; } } }, /** * 验证整个表单 */ validateForm: function () { var _self = this; var usernameValid = _self.validateUsername(); var passwordValid = _self.validatePassword(); return usernameValid && passwordValid; }, /** * 初始化页面数据 */ initData: function () { var _self = this; console.log('初始化数据同步页面'); // 初始化 IndexDB 数据库 var objectStoreDefines = [ { name: 'checkVouchList', defineOption: { keyPath: 'checkVouchId' }, indexDefineList: null }, { name: 'uploadQueue', defineOption: { keyPath: 'checkVouchId' }, indexDefineList: null } ]; _self.checkVouchDb = new IndexDbFactory('checkVouchDB', objectStoreDefines, 1); _self.checkVouchDb.open(function () { console.log('盘点数据库初始化成功'); }, function () { console.error('盘点数据库初始化失败'); vant.Dialog.alert({ title: '错误', message: '数据库初始化失败,数据同步功能不可用。', theme: 'round-button', }); }); } }, created: function () { var _self = this; var clientId = localStorage.getItem("clientId"); if (clientId != null && clientId != '') { _self.clientId = clientId; } console.log('DataSync Vue实例创建完成'); }, mounted: function () { var _self = this; if(!plugin.urlApi.getApiUrl()){ const ip = Common.getRootPath(); // const ip = 'http://b374733e.natappfree.cc' localStorage.setItem("ipServer", ip); } else { localStorage.setItem("ipServer", plugin.urlApi.getApiUrl()); } _self.initData(); console.log('DataSync页面加载完成'); // 隐藏页面加载指示器 setTimeout(function() { var loadingElement = document.getElementById('pageLoading'); if (loadingElement) { loadingElement.style.display = 'none'; } }, 100); } });