const packageJSON = require('../package.json') const fs = require('fs') const path = require('path') const KeyCode = require('./keyCode').KeyCode const axios = require('axios') const childProcess = require('child_process') const vsprintf = require('sprintf-js').vsprintf const {remote} = require('electron') const translate = (key) => Editor.T(`${packageJSON.name}.${key}`) // panel/index.js, this filename needs to match the one registered in package.json Editor.Panel.extend({ // css style for panel style: fs.readFileSync(Editor.url('packages://user-guide/static/style/default/index.css', 'utf8')), // html template for panel template: fs.readFileSync(Editor.url('packages://user-guide/static/template/default/index.html', 'utf8')), // element and variable binding $: { app: '#app', }, // method executed when template and styles are successfully loaded and initialized ready() { new window.Vue({ el: this.shadowRoot, data: { step1I18n: translate('step1'), titleI18n: translate('title'), guideI18n: translate('guide'), moveUpI18n: translate('moveUp'), moveDownI18n: translate('moveDown'), deleteI18n: translate('delete'), touchI18n: translate('touch'), keyI18n: translate('key'), useLastGuideI18n: translate('useLastGuide'), addI18n: translate('add'), confirmI18n: translate('confirm'), step2I18n: translate('step2'), guideSignI18n: translate('guideSign'), textBgI18n: translate('textBg'), goBackI18n: translate('goBack'), editImg: path.join(__dirname, "../static/images/edit.png"), selectImg: path.join(__dirname, "../static/images/select.png"), helpImg: path.join(__dirname, "../static/images/help.png"), prevplayImg: path.join(__dirname, "../static/images/prevplay.png"), addImg: path.join(__dirname, "../static/images/add.png"), confirmImg: path.join(__dirname, "../static/images/confirm.png"), backImg: path.join(__dirname, "../static/images/back.png"), closeImg: path.join(__dirname, "../static/images/close.png"), currentStep: 1, guideTextType: 'zh', guideArray: [ { "nodeUUID": "", "path": "", "key": "", "guideText": "", "guideEnText": "", "guideType": "touch", "isDynamic": false, "isAliased": false, "alias": "", "uiType": "", "arrowPos": [0, 0], "arrowRotation": 0, "textBgPos": [0, 0], "arrowType": 0, "showTextBg": false, "showRole": false, "prevent": true, } ], guideSignPicArray: [ { "name": `${translate("guideSign")} 1`, "type": "frame", "path": path.join(__dirname, "../static/images/frame1.png"), "value": true }, { "name": `${translate("guideSign")} 2`, "type": "frame", "path": path.join(__dirname, "../static/images/frame2.png"), "value": false }, { "name": `${translate("guideSign")} 3`, "type": "frame", "path": path.join(__dirname, "../static/images/frame3.png"), "value": false }, { "name": `${translate("guideSign")} 4`, "type": "frame", "path": path.join(__dirname, "../static/images/frame4.png"), "value": false }, { "name": `${translate("guideSign")} 5`, "type": "frame", "path": path.join(__dirname, "../static/images/frame5.png"), "value": false }, { "name": `${translate("guideSign")} 6`, "type": "frame", "path": path.join(__dirname, "../static/images/frame6.png"), "value": false }, { "name": `${translate("guideSign")} 7`, "type": "frame", "path": path.join(__dirname, "../static/images/frame7.png"), "value": false }, { "name": `${translate("guideSign")} 8`, "type": "frame", "path": path.join(__dirname, "../static/images/frame8.png"), "value": false }, { "name": `${translate("guideSign")} 9`, "type": "frame", "path": path.join(__dirname, "../static/images/frame9.png"), "value": false }, { "name": `${translate("guideSign")} 10`, "type": "frame", "path": path.join(__dirname, "../static/images/frame10.png"), "value": false }, { "name": `${translate("guideSign")} 11`, "type": "hand", "path": path.join(__dirname, "../static/images/hand1.png"), "value": false }, { "name": `${translate("guideSign")} 12`, "type": "hand", "path": path.join(__dirname, "../static/images/hand2.png"), "value": false }, { "name": `${translate("guideSign")} 13`, "type": "hand", "path": path.join(__dirname, "../static/images/hand3.png"), "value": false }, { "name": `${translate("guideSign")} 14`, "type": "hand", "path": path.join(__dirname, "../static/images/hand4.png"), "value": false }, { "name": `${translate("guideSign")} 15`, "type": "hand", "path": path.join(__dirname, "../static/images/hand5.png"), "value": false }, { "name": `${translate("guideSign")} 16`, "type": "hand", "path": path.join(__dirname, "../static/images/hand6.png"), "value": false }, { "name": `${translate("guideSign")} 17`, "type": "hand", "path": path.join(__dirname, "../static/images/hand7.png"), "value": false }, { "name": `${translate("guideSign")} 18`, "type": "hand", "path": path.join(__dirname, "../static/images/hand8.png"), "value": false }, { "name": `${translate("guideSign")} 19`, "type": "hand", "path": path.join(__dirname, "../static/images/hand9.png"), "value": false }, { "name": `${translate("guideSign")} 20`, "type": "hand", "path": path.join(__dirname, "../static/images/hand10.png"), "value": false }, { "name": `${translate("guideSign")} 21`, "type": "hand", "path": path.join(__dirname, "../static/images/hand11.png"), "value": false }, { "name": `${translate("guideSign")} 22`, "type": "hand", "path": path.join(__dirname, "../static/images/hand12.png"), "value": false }, { "name": `${translate("guideSign")} 23`, "type": "arrow", "path": path.join(__dirname, "../static/images/arrow1.png"), "value": false }, { "name": `${translate("guideSign")} 24`, "type": "arrow", "path": path.join(__dirname, "../static/images/arrow2.png"), "value": false }, { "name": `${translate("guideSign")} 25`, "type": "arrow", "path": path.join(__dirname, "../static/images/arrow3.png"), "value": false }, { "name": `${translate("guideSign")} 26`, "type": "arrow", "path": path.join(__dirname, "../static/images/arrow4.png"), "value": false }, { "name": `${translate("guideSign")} 27`, "type": "arrow", "path": path.join(__dirname, "../static/images/arrow5.png"), "value": false }, { "name": `${translate("guideSign")} 28`, "type": "arrow", "path": path.join(__dirname, "../static/images/arrow6.png"), "value": false }, { "name": `${translate("guideSign")} 29`, "type": "arrow", "path": path.join(__dirname, "../static/images/arrow7.png"), "value": false }, { "name": `${translate("guideSign")} 30`, "type": "arrow", "path": path.join(__dirname, "../static/images/arrow8.png"), "value": false }, { "name": `${translate("guideSign")} 31`, "type": "arrow", "path": path.join(__dirname, "../static/images/arrow9.png"), "value": false }, { "name": `${translate("guideSign")} 32`, "type": "arrow", "path": path.join(__dirname, "../static/images/arrow10.png"), "value": false }, { "name": `${translate("guideSign")} 33`, "type": "arrow", "path": path.join(__dirname, "../static/images/arrow11.png"), "value": false }, { "name": `${translate("guideSign")} 34`, "type": "arrow", "path": path.join(__dirname, "../static/images/arrow12.png"), "value": false }, ], selectedGuideSignPicPath: "", selectedGuideSignType: "", textBgPicArray: [ { 'name': `${translate("textBg")} 1`, 'path': path.join(__dirname, '../static/images/bg1.png'), 'value': true }, { 'name': `${translate("textBg")} 2`, 'path': path.join(__dirname, '../static/images/bg2.png'), 'value': false }, { 'name': `${translate("textBg")} 3`, 'path': path.join(__dirname, '../static/images/bg3.png'), 'value': false }, { 'name': `${translate("textBg")} 4`, 'path': path.join(__dirname, '../static/images/bg4.png'), 'value': false }, { 'name': `${translate("textBg")} 5`, 'path': path.join(__dirname, '../static/images/bg5.png'), 'value': false }, { 'name': `${translate("textBg")} 6`, 'path': path.join(__dirname, '../static/images/bg6.png'), 'value': false }, { 'name': `${translate("textBg")} 7`, 'path': path.join(__dirname, '../static/images/bg7.png'), 'value': false }, { 'name': `${translate("textBg")} 8`, 'path': path.join(__dirname, '../static/images/bg8.png'), 'value': false }, { 'name': `${translate("textBg")} 9`, 'path': path.join(__dirname, '../static/images/bg9.png'), 'value': false }, { 'name': `${translate("textBg")} 10`, 'path': path.join(__dirname, '../static/images/bg10.png'), 'value': false }, { 'name': `${translate("textBg")} 11`, 'path': path.join(__dirname, '../static/images/bg11.png'), 'value': false }, { 'name': `${translate("textBg")} 12`, 'path': path.join(__dirname, '../static/images/bg12.png'), 'value': false }, { 'name': `${translate("textBg")} 13`, 'path': path.join(__dirname, '../static/images/bg13.png'), 'value': false }, { 'name': `${translate("textBg")} 14`, 'path': path.join(__dirname, '../static/images/bg14.png'), 'value': false }, { 'name': `${translate("textBg")} 15`, 'path': path.join(__dirname, '../static/images/bg15.png'), 'value': false }, { 'name': `${translate("textBg")} 16`, 'path': path.join(__dirname, '../static/images/bg16.png'), 'value': false } ], selectedTextBgPicPath: "", }, created() { // 如果不需要更新提示,注释这行代码即可 //this.checkUpdate(); // 用户如果没有进行选择,则默认选择数组中第一个元素 this.selectedGuideSignPicPath = this.guideSignPicArray[0].path; this.selectedGuideSignType = this.guideSignPicArray[0].type; this.selectedTextBgPicPath = this.textBgPicArray[0].path; }, methods: { checkUpdate() { // 检查是否有更新 axios.get('https://la-vie.gitee.io/user-guide2/package.json', {headers: {'Cache-Control': 'no-cache'}}) .then(function (response) { let remoteVersion = response.data.version; if (packageJSON.version != remoteVersion) { Editor.warn(`[v${remoteVersion}] ${translate('updateWarn')}`); } }); }, changeGuideTextType() { // 切换引导语言类型 if (this.guideTextType == 'zh') { this.guideTextType = 'en' } else { this.guideTextType = 'zh' } }, showAliasInput(index) { // 显示别名设置输入框 this.guideArray[index].isAliased = true }, onArrowPosXChange(value, index) { this.guideArray[index].arrowPos[0] = parseInt(value) ? parseInt(value) : value == '0' ? 0 : value }, onArrowPosYChange(value, index) { this.guideArray[index].arrowPos[1] = parseInt(value) ? parseInt(value) : value == '0' ? 0 : value }, onArrowRotationChange(value, index) { this.guideArray[index].arrowRotation = parseInt(value) ? parseInt(value) : value == '0' ? 0 : value }, onTextBgPosXChange(value, index) { this.guideArray[index].textBgPos[0] = parseInt(value) ? parseInt(value) : value == '0' ? 0 : value }, onTextBgPosYChange(value, index) { this.guideArray[index].textBgPos[1] = parseInt(value) ? parseInt(value) : value == '0' ? 0 : value }, onArrowTypeChange(value, index) { this.guideArray[index].arrowType = parseInt(value) ? parseInt(value) : value == '0' ? 0 : value }, onShowTextBgChange(value, index) { this.guideArray[index].showTextBg = value }, onShowRoleChange(value, index) { // 显示别名设置输入框 this.guideArray[index].showRole = value }, onPreventChange(value, index) { // 显示别名设置输入框 this.guideArray[index].prevent = value }, hideAliasInput(index) { // 隐藏别名设置输入框,并清空内容 this.guideArray[index].isAliased = false this.guideArray[index].alias = '' }, setAlias(value, index) { // 设置别名 this.guideArray[index].alias = value.trim() }, onUiTypeChange(value, index) { // 设置别名 this.guideArray[index].uiType = value.trim() }, moveUp(index) { // 将当前引导往上移动 if (index == 0) { return } let temp = this.guideArray[index] this.guideArray[index] = this.guideArray[index - 1] this.guideArray[index - 1] = temp guideArrayTemp = JSON.parse(JSON.stringify(this.guideArray)) this.guideArray = Object.assign([], [], guideArrayTemp) }, moveDown(index) { // 将当前引导往下移动 if (index == this.guideArray.length - 1) { return } let temp = this.guideArray[index] this.guideArray[index] = this.guideArray[index + 1] this.guideArray[index + 1] = temp guideArrayTemp = JSON.parse(JSON.stringify(this.guideArray)) this.guideArray = Object.assign([], [], guideArrayTemp) }, addGuide() { // 新增引导 this.guideArray.push( { "nodeUUID": "", "path": "", "key": "", "guideText": "", "guideEnText": "", "guideType": "touch", "isDynamic": false, "isAliased": false, "alias": "", "uiType": "", "arrowPos": [0, 0], "arrowRotation": 0, "textBgPos": [0, 0], "arrowType": 0, "showTextBg": false, "showRole": false, "prevent": true, } ) }, deleteGuide(index) { // 删除一条引导 // 必须要有一个引导存在 if (this.guideArray.length == 1) { remote.dialog.showMessageBoxSync({ type: 'warning', buttons: ['Ok'], title: '', message: translate("noGuideArrayWarning") }) return } this.guideArray.splice(index, 1) }, onGuideTypeSelectionChange(value, index) { // 更新引导类型(触摸还是按键) this.guideArray[index].guideType = value }, switchBetweenStaticAndDynamic(index) { // 在静态和动态节点之间切换 this.guideArray[index].isDynamic = !this.guideArray[index].isDynamic }, onNodeSelectionChange(uuid, index) { // 更新节点UUID this.guideArray[index].nodeUUID = uuid }, onDynamicNodePathChange(path, index) { // 更新动态节点的路径 this.guideArray[index].path = path }, onKeyChange(key, index) { // 更新按键 this.guideArray[index].key = key.trim() }, showKeyHelp() { // 显示按键帮助 childProcess.exec('start https://docs.cocos.com/creator/api/zh/enumeration/KeyCode') console.log(translate('keyHelp')) }, onGuideTextChange(text, index) { // 更新引导文本 if (this.guideTextType == 'zh') { this.guideArray[index].guideText = text } else { this.guideArray[index].guideEnText = text } }, onGuideSignCheckboxConfirm(index) { // 选择引导图片 for (let i = 0; i < this.guideSignPicArray.length; i++) { this.guideSignPicArray[i].value = false } this.guideSignPicArray[index].value = true this.selectedGuideSignPicPath = this.guideSignPicArray[index].path this.selectedGuideSignType = this.guideSignPicArray[index].type }, onTextBgCheckboxConfirm(index) { // 选择引导文本背景图片 for (let i = 0; i < this.textBgPicArray.length; i++) { this.textBgPicArray[i].value = false } this.textBgPicArray[index].value = true this.selectedTextBgPicPath = this.textBgPicArray[index].path }, back() { // 返回上一个引导步骤 this.currentStep -= 1 }, generateResources() { //通过预制体显示,不需要动态图片资源 return // 将图片文件存放到resources/user-guide文件夹下 let resourcesPath = Editor.url('db://assets/resources') if (!fs.existsSync(resourcesPath)) { Editor.assetdb.createOrSave('db://assets/resources') } let userGuideInResourcesPath = Editor.url('db://assets/resources/user-guide') if (!fs.existsSync(userGuideInResourcesPath)) { Editor.assetdb.createOrSave('db://assets/resources/user-guide') } fs.copyFileSync(this.selectedGuideSignPicPath, path.join(__dirname, '../static/images/guideSign.png')) fs.copyFileSync(this.selectedTextBgPicPath, path.join(__dirname, '../static/images/textBg.png')) Editor.assetdb.import([ path.join(__dirname, '../static/images/guideSign.png'), path.join(__dirname, '../static/images/textBg.png'), path.join(__dirname, '../static/images/default_btn_normal.png') ], 'db://assets/resources/user-guide') }, generateTsCode() { // 生成js和ts代码,开发者将其挂载到Canvas节点上然后在自己的脚本中调用showGuide()方法即可 let self = this Editor.Ipc.sendToPanel('scene', 'scene:query-hierarchy', (err, sceneID, hierarchy) => { if (err) { Editor.error(translate('hierarchyError')) return } // 获取到场景名称 Editor.assetdb.queryInfoByUuid(sceneID, function (err, info) { let sceneName = "" if (err) { Editor.error(translate('sceneNameError')) sceneName = sceneID } else if (!info) { sceneName = 'main' } else { sceneName = info.path.replace(/\\/g, '/').split('/').pop().replace('.fire', '').replace(/\s*/g, '') } // 获取UUID对应的节点路径 for (let i = 0; i < self.guideArray.length; i++) { // if (sceneID.length <= 25) { // Editor.error(translate("dynacmicNodeError")) // break // } let nodeUUID = self.guideArray[i].nodeUUID for (let j = 0; j < hierarchy.length; j++) { if (hierarchy[j].name == 'Canvas') { // Editor.log(Object.keys(hierarchy[j].children[1].children[1])) // Editor.log(Object.values(hierarchy[j].children[1].children[1])) let allCanvasNodesArray = hierarchy[j].children // 如果是动态节点,开发者会手动输入路径,无需赋值 if (!self.guideArray[i].isDynamic) { self.guideArray[i].path = self.getNodePathByUUID(allCanvasNodesArray, nodeUUID).path } break } } } // 因为下面会将guideArray中的键名称改为键值 // 开发者如果生成了ts文件,又返回第一步想要修改部分按键的话会发现键名称已经改成了键值,这样就不好修改 // 为了防止这种情况出现,这里用一个临时的变量代替guideArray let guideArrayTemp = JSON.parse(JSON.stringify(self.guideArray)) // 将按键名称转换成键值 for (let i = 0; i < guideArrayTemp.length; i++) { if (!guideArrayTemp[i].key) { continue } let keyCodes = '' let keys = guideArrayTemp[i].key.split('+') for (let j = 0; j < keys.length; j++) { if (j == keys.length - 1) { keyCodes += KeyCode[keys[j]] } else { keyCodes += (KeyCode[keys[j]] + '+') } } guideArrayTemp[i].key = keyCodes } // 生成最终的数据 let guideData = { "nodesAndTexts": guideArrayTemp, "guideSignType": self.selectedGuideSignType, } let tsCodePath = 'db://assets/script/userguide' // 生成代码文件 let userGuideCodeFolderPath = Editor.url(tsCodePath) if (!fs.existsSync(userGuideCodeFolderPath)) { Editor.assetdb.createOrSave(tsCodePath) } sceneNameFolderPath = Editor.url(`${tsCodePath}`) if (!fs.existsSync(sceneNameFolderPath)) { Editor.assetdb.createOrSave(`${tsCodePath}`) } // let urlJS = `db://assets/user-guide/${sceneName}/UserGuide.js`; // let codesJS = vsprintf(fs.readFileSync(path.join(__dirname, "../static/codes/UserGuideJS.txt"), "utf-8"), [JSON.stringify(guideData)]); // Editor.assetdb.createOrSave(urlJS, codesJS); let urlTS = `${tsCodePath}/UserGuide.ts`; let codesTS = vsprintf(fs.readFileSync(path.join(__dirname, "../static/codes/UserGuideTS.txt"), "utf-8"), [JSON.stringify(guideData)]); Editor.assetdb.createOrSave(urlTS, codesTS); }) }); }, getNodePathByUUID(canvasNodesArray, uuid, path = 'Canvas') { // 根据节点UUID获取其路径 let pathTemp = path result = { path: '', isOk: false } for (let i = 0; i < canvasNodesArray.length; i++) { path = pathTemp + '/' + canvasNodesArray[i].name if (canvasNodesArray[i].id == uuid) { return { path: path, isOk: true } } else if (canvasNodesArray[i].children && canvasNodesArray[i].children.length) { result = this.getNodePathByUUID(canvasNodesArray[i].children, uuid, path) if (result.isOk) { return result } } } return result }, useLastGuide() { // 使用上次的引导 let choice = remote.dialog.showMessageBoxSync({ type: 'info', buttons: ['OK', 'Cancel'], title: '', message: translate('confirmUseLastGuide') }) if (choice.response == 1) { return } if (!localStorage.getItem('UserGuideExt')) { remote.dialog.showMessageBoxSync({ type: 'info', buttons: ['OK'], title: '', message: translate("lastGuideNotExisted") }) return } this.guideArray = Object.assign([], [], JSON.parse(localStorage.getItem('UserGuideExt'))) Editor.Panel.popup('user-guide') }, confirmStep1() { // 确认引导的第一步骤 // 引导节点和文本不能都空着 for (let i = 0; i < this.guideArray.length; i++) { if (this.guideArray[i].guideType == 'touch' && !this.guideArray[i].nodeUUID && !this.guideArray[i].path && !this.guideArray[i].guideText && !this.guideArray[i].guideEnText) { // remote.dialog.showMessageBoxSync({ // type: 'warning', // buttons: ['OK'], // title: '', // message: translate("emptyNodeTextGuideWarning") // }) // return } else if (this.guideArray[i].guideType == 'key' && !this.guideArray[i].key && !this.guideArray[i].guideText && !this.guideArray[i].guideEnText) { remote.dialog.showMessageBoxSync({ type: 'warning', buttons: ['OK'], title: '', message: translate("emptyKeyTextGuideWarning") }) return } } // 检查别名是否有重复 let tempArray = [] for (let i = 0; i < this.guideArray.length; i++) { if (this.guideArray[i].alias == '') { continue } if (tempArray.indexOf(this.guideArray[i].alias) > -1) { remote.dialog.showMessageBoxSync({ type: 'error', buttons: ['OK'], title: '', message: `${translate('aliasRepeatError')}${tempArray.indexOf(this.guideArray[i].alias) + 1}, ${i + 1}` }) return } else { tempArray.push(this.guideArray[i].alias) } } // 检查按键有没有错误 for (let i = 0; i < this.guideArray.length; i++) { if (this.guideArray[i].key) { let keys = this.guideArray[i].key.split('+') for (let j = 0; j < keys.length; j++) { if (KeyCode[keys[j]] == undefined) { remote.dialog.showMessageBoxSync({ type: 'error', buttons: ['OK'], title: '', message: `${translate('guide')} ${i + 1} ${translate('keyError')}${keys[j]}` }) return } } } } // 将当前引导存储到本地 localStorage.setItem('UserGuideExt', JSON.stringify(this.guideArray)) // // 进入下一步 // this.currentStep += 1 this.confirmStep2() }, confirmStep2() { // 确认引导的第二步骤 // 生成文件 this.generateResources() this.generateTsCode() // 显示提示 remote.dialog.showMessageBoxSync({ type: 'info', buttons: ['OK'], title: '', message: translate('generateSuccess') }) } } }); }, // register your ipc messages here messages: { // 'user-guide:hello' (event) { // this.$label.innerText = 'Hello!'; // } } });