添加快捷命令服务的功能

This commit is contained in:
fofolee 2022-04-17 18:14:51 +08:00
parent 7390125d7c
commit 661c6b3711
16 changed files with 878 additions and 498 deletions

View File

@ -9,6 +9,8 @@ const {
} = require('./lib/vm2')
const path = require("path")
const axios = require('axios');
const http = require('http');
const url = require('url')
window._ = require("lodash")
window.yuQueClient = axios.create({
@ -266,154 +268,6 @@ let getSleepCodeByShell = ms => {
return cmd
}
// 屏蔽危险函数
window.getuToolsLite = () => {
var utoolsLite = Object.assign({}, utools)
if (utools.isDev()) return utoolsLite
// 数据库相关接口
delete utoolsLite.db
delete utoolsLite.dbStorage
delete utoolsLite.removeFeature
delete utoolsLite.setFeature
delete utoolsLite.onDbPull
// 支付相关接口
delete utoolsLite.fetchUserServerTemporaryToken
delete utoolsLite.getUserServerTemporaryToken
delete utoolsLite.openPayment
delete utoolsLite.fetchUserPayments
return utoolsLite
}
let getSandboxFuns = () => {
var sandbox = {
utools: getuToolsLite(),
quickcommand: quickcommand,
electron: electron,
axios: axios,
Audio: Audio,
fetch: fetch
}
shortCodes.forEach(f => {
sandbox[f.name] = f
})
return sandbox
}
let createNodeVM = () => {
var sandbox = getSandboxFuns()
const vm = new NodeVM({
require: {
external: true,
builtin: ["*"],
},
console: 'redirect',
env: process.env,
sandbox: sandbox,
});
return vm
}
let stringifyAll = item => {
var cache = [];
var string = JSON.stringify(item, (key, value) => {
if (typeof value === 'object' && value !== null) {
if (cache.indexOf(value) !== -1) return
cache.push(value);
}
return value;
}, '\t')
if (string != "{}") return string
else return item.toString()
}
let parseItem = item => {
if (typeof item == "object") {
if (Buffer.isBuffer(item)) {
var bufferString = `[Buffer ${item.slice(0, 50).toString('hex').match(/\w{1,2}/g).join(" ")}`
if (item.length > 50) bufferString += `... ${(item.length / 1000).toFixed(2)}kb`
return bufferString + ']'
} else if (item instanceof ArrayBuffer) {
return `ArrayBuffer(${item.byteLength})`
} else if (item instanceof Blob) {
return `Blob {size: ${item.size}, type: "${item.type}"}`
} else {
try {
return stringifyAll(item)
} catch (error) {}
}
} else if (typeof item == "undefined") {
return "undefined"
}
return item.toString()
}
window.convertFilePathToUtoolsPayload = files => files.map(file => {
let isFile = fs.statSync(file).isFile()
return {
isFile: isFile,
isDirectory: !isFile,
name: path.basename(file),
path: file
}
})
let parseStdout = stdout => stdout.map(x => parseItem(x)).join("\n")
window.VmEval = (cmd, sandbox = {}) => new VM({
sandbox: sandbox
}).run(cmd)
// The vm module of Node.js is deprecated in the renderer process and will be removed
window.runCodeInVm = (cmd, cb) => {
const vm = createNodeVM()
//重定向 console
vm.on('console.log', (...stdout) => {
console.log(stdout);
cb(parseStdout(stdout), null)
});
vm.on('console.error', stderr => {
cb(null, stderr.toString())
});
let liteErr = e => {
if (!e) return
return e.stack.replace(/([ ] +at.+)|(.+\.js:\d+)/g, '').trim()
}
// 错误处理
try {
vm.run(cmd, path.join(__dirname, 'preload.js'));
} catch (e) {
console.log('Error: ', e)
cb(null, liteErr(e))
}
let cbUnhandledError = e => {
removeAllListener()
console.log('UnhandledError: ', e)
cb(null, liteErr(e.error))
}
let cbUnhandledRejection = e => {
removeAllListener()
console.log('UnhandledRejection: ', e)
cb(null, liteErr(e.reason))
}
let removeAllListener = () => {
window.removeEventListener('error', cbUnhandledError)
window.removeEventListener('unhandledrejection', cbUnhandledRejection)
delete window.isWatchingError
}
if (!window.isWatchingError) {
window.addEventListener('error', cbUnhandledError)
window.addEventListener('unhandledrejection', cbUnhandledRejection)
window.isWatchingError = true
}
}
window.htmlEncode = (value) => {
return String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;")
}
@ -509,16 +363,6 @@ window.getCurrentFolderPathFix = () => {
return pwdFix.replace(/\\/g, '\\\\')
}
window.getMatchedFilesFix = payload => {
let MatchedFiles = payload
let Matched = cmd.match(/\{\{MatchedFiles(\[\d+\]){0,1}(\.\w{1,11}){0,1}\}\}/g)
Matched && Matched.forEach(m => {
repl = eval(m.slice(2, -2))
typeof repl == 'object' ? (repl = JSON.stringify(repl)) : (repl = repl.replace('\\', '\\\\'))
cmd = cmd.replace(m, repl.replace('$', '$$$'))
})
}
window.saveFile = (content, file) => {
if (file instanceof Object) file = utools.showSaveDialog(file)
if (!file) return false
@ -559,6 +403,156 @@ window.getSelectFile = hwnd => {
window.clipboardReadText = () => electron.clipboard.readText()
window.convertFilePathToUtoolsPayload = files => files.map(file => {
let isFile = fs.statSync(file).isFile()
return {
isFile: isFile,
isDirectory: !isFile,
name: path.basename(file),
path: file
}
})
let stringifyAll = item => {
var cache = [];
var string = JSON.stringify(item, (key, value) => {
if (typeof value === 'object' && value !== null) {
if (cache.indexOf(value) !== -1) return
cache.push(value);
}
return value;
}, '\t')
if (string != "{}") return string
else return item.toString()
}
let parseItem = item => {
if (typeof item == "object") {
if (Buffer.isBuffer(item)) {
var bufferString = `[Buffer ${item.slice(0, 50).toString('hex').match(/\w{1,2}/g).join(" ")}`
if (item.length > 50) bufferString += `... ${(item.length / 1000).toFixed(2)}kb`
return bufferString + ']'
} else if (item instanceof ArrayBuffer) {
return `ArrayBuffer(${item.byteLength})`
} else if (item instanceof Blob) {
return `Blob {size: ${item.size}, type: "${item.type}"}`
} else {
try {
return stringifyAll(item)
} catch (error) {}
}
} else if (typeof item == "undefined") {
return "undefined"
}
return item.toString()
}
let parseStdout = stdout => stdout.map(x => parseItem(x)).join("\n")
// 屏蔽危险函数
window.getuToolsLite = () => {
var utoolsLite = Object.assign({}, utools)
if (utools.isDev()) return utoolsLite
// 数据库相关接口
delete utoolsLite.db
delete utoolsLite.dbStorage
delete utoolsLite.removeFeature
delete utoolsLite.setFeature
delete utoolsLite.onDbPull
// 支付相关接口
delete utoolsLite.fetchUserServerTemporaryToken
delete utoolsLite.getUserServerTemporaryToken
delete utoolsLite.openPayment
delete utoolsLite.fetchUserPayments
return utoolsLite
}
let getSandboxFuns = () => {
var sandbox = {
utools: getuToolsLite(),
quickcommand: quickcommand,
electron: electron,
axios: axios,
Audio: Audio,
fetch: fetch
}
shortCodes.forEach(f => {
sandbox[f.name] = f
})
return sandbox
}
let createNodeVM = (userVars) => {
var sandbox = getSandboxFuns()
Object.assign(userVars, sandbox)
const vm = new NodeVM({
require: {
external: true,
builtin: ["*"],
},
console: 'redirect',
env: process.env,
sandbox: userVars,
});
return vm
}
window.VmEval = (cmd, sandbox = {}) => new VM({
sandbox: sandbox
}).run(cmd)
let isWatchingError = false
// The vm module of Node.js is deprecated in the renderer process and will be removed
window.runCodeInVm = (cmd, callback, userVars = {}) => {
const vm = createNodeVM(userVars)
//重定向 console
vm.on('console.log', (...stdout) => {
console.log(stdout);
callback(parseStdout(stdout), null)
});
vm.on('console.error', stderr => {
callback(null, stderr.toString())
});
let liteErr = e => {
if (!e) return
return e.stack.replace(/([ ] +at.+)|(.+\.js:\d+)/g, '').trim()
}
// 错误处理
try {
vm.run(cmd, path.join(__dirname, 'preload.js'));
} catch (e) {
console.log('Error: ', e)
callback(null, liteErr(e))
}
let cbUnhandledError = e => {
removeAllListener()
console.log('UnhandledError: ', e)
callback(null, liteErr(e.error))
}
let cbUnhandledRejection = e => {
removeAllListener()
console.log('UnhandledRejection: ', e)
callback(null, liteErr(e.reason))
}
let removeAllListener = () => {
window.removeEventListener('error', cbUnhandledError)
window.removeEventListener('unhandledrejection', cbUnhandledRejection)
isWatchingError = false
}
if (!isWatchingError) {
window.addEventListener('error', cbUnhandledError)
window.addEventListener('unhandledrejection', cbUnhandledRejection)
isWatchingError = true
}
}
window.runCodeFile = (cmd, option, terminal, callback) => {
var bin = option.bin,
argv = option.argv,
@ -609,4 +603,61 @@ window.runCodeFile = (cmd, option, terminal, callback) => {
// let stderr = err_chunks.join("");
// callback(stdout, stderr)
// })
}
let httpServer
window.quickcommandHttpServer = () => {
let run = (cmd = '', port = 33442) => {
let httpResponse = (res, code, result) => {
// 因为无法判断 vm2 是否执行完毕,故只收受一次 console.log接收后就关闭连接
if (res.finished) return
res.writeHead(code, {
'Content-Type': 'text/html'
});
if (result) res.write(result);
res.end();
}
let runUserCode = (res, cmd, userVars) => {
// 不需要返回输出的提前关闭连接
if (!cmd.includes('console.log')) httpResponse(res, 200)
window.runCodeInVm(cmd, (stdout, stderr) => {
// 错误返回 500
if (stderr) return httpResponse(res, 500, stderr)
return httpResponse(res, 200, stdout)
}, userVars)
}
httpServer = http.createServer()
httpServer.on('request', (req, res) => {
if (req.method === 'GET') {
let parsedParams = _.cloneDeep(url.parse(req.url, true).query)
runUserCode(res, cmd, parsedParams)
} else if (req.method === 'POST') {
let data = []
req.on('data', (chunk) => {
data.push(chunk)
})
req.on('end', () => {
let parsedParams
let params = data.join("").toString()
// 先尝试作为 json 解析
try {
parsedParams = JSON.parse(params)
} catch (error) {
parsedParams = _.cloneDeep(url.parse('?' + params, true).query)
}
runUserCode(res, cmd, parsedParams)
})
} else {
httpResponse(res, 405)
}
})
httpServer.listen(port, 'localhost');
}
let stop = () => {
httpServer.close()
}
return {
run,
stop
}
}

View File

@ -33,6 +33,16 @@ export default defineComponent({
});
return;
}
//
if (this.$profile.quickFeatures.apiServer.serverStatus) {
window
.quickcommandHttpServer()
.run(
this.$profile.quickFeatures.apiServer.cmd,
this.$profile.quickFeatures.apiServer.port
);
console.log("Server Start...");
}
//
this.setCssVar("primary", this.$profile.primaryColor);
//

BIN
src/assets/feature/api.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -10,7 +10,7 @@ import Cron from "croner"
let userProfile = UTOOLS.getDB(
UTOOLS.DBPRE.CFG + "preferences"
);
Object.assign(defaultProfile, userProfile)
_.merge(defaultProfile, _.cloneDeep(userProfile))
// "async" is optional;
// more info on params: https://v2.quasar.dev/quasar-cli/boot-files

View File

@ -143,6 +143,7 @@
</div>
<MonacoEditor
class="absolute-bottom"
:placeholder="true"
ref="editor"
@typing="(val) => (quickcommandInfo.cmd = val)"
:style="{

View File

@ -12,6 +12,176 @@
/>
<!-- 菜单 -->
<q-list>
<!-- 实用功能 -->
<q-item clickable>
<q-item-section side>
<q-icon name="keyboard_arrow_left" />
</q-item-section>
<q-item-section>实用功能</q-item-section>
<q-menu anchor="top end" self="top start">
<q-list>
<q-item>
<q-item-section side>
<q-icon name="folder_special" />
</q-item-section>
<q-input
dense
prefix="快速收藏文件至"
suffix="标签"
outlined
input-class="text-center"
style="width: 280px"
autofocus
v-model="quickFeatures.favFile.tag"
type="text"
>
<template v-slot:append>
<q-toggle
@click="toggleFeature('favFile')"
v-model="quickFeatures.favFile.enable"
checked-icon="check"
color="primary"
/>
</template>
<q-tooltip
>启用后选中文件可以通过超级面板快速将文件收藏到{{
quickFeatures.favFile.tag
}}标签
</q-tooltip>
</q-input>
</q-item>
<q-item>
<q-item-section side>
<q-icon name="bookmarks" />
</q-item-section>
<q-input
dense
prefix="快速收藏网址至"
suffix="标签"
outlined
input-class="text-center"
style="width: 280px"
v-model="quickFeatures.favUrl.tag"
type="text"
>
<template v-slot:append>
<q-toggle
@click="toggleFeature('favUrl')"
v-model="quickFeatures.favUrl.enable"
checked-icon="check"
color="primary"
/>
</template>
<q-tooltip
>启用后在浏览器界面可以通过超级面板快速将网址收藏到{{
quickFeatures.favUrl.tag
}}标签
</q-tooltip>
</q-input>
</q-item>
<q-item>
<q-item-section side>
<q-icon name="drive_file_rename_outline" />
</q-item-section>
<q-input
dense
prefix="新建插件别名至"
suffix="标签"
outlined
input-class="text-center"
style="width: 280px"
autofocus
v-model="quickFeatures.pluNickName.tag"
type="text"
>
<template v-slot:append>
<q-toggle
@click="toggleFeature('pluNickName')"
v-model="quickFeatures.pluNickName.enable"
checked-icon="check"
color="primary"
/>
</template>
<q-tooltip
>启用后在主输入框输入插件别名可以快速设置插件别名<br />
并将所有设置的别名保存至{{
quickFeatures.pluNickName.tag
}}标签
</q-tooltip>
</q-input>
</q-item>
<q-item>
<q-item-section side>
<q-icon name="timer" />
</q-item-section>
<q-input
dense
prefix="新建计划任务至"
suffix="标签"
outlined
input-class="text-center"
style="width: 280px"
v-model="quickFeatures.crontab.tag"
type="text"
>
<template v-slot:append>
<q-toggle
@click="toggleFeature('crontab')"
v-model="quickFeatures.crontab.enable"
checked-icon="check"
color="primary"
/>
</template>
<q-tooltip
>启用后在主输入框输入计划任务可以配置计划任务定制执行指定或新建的快捷命令<br />
如果是直接新建则新建的任务会保存在{{
quickFeatures.crontab.tag
}}标签<br />
注意此功能并没有使用系统自带的计划任务需要配置插件跟随
utools 启动和保留后台<br />
本功能比系统自带的更为强大因为你可以在计划任务中任意使用
utools quickcommand api
</q-tooltip>
</q-input>
</q-item>
<q-item>
<q-item-section side>
<q-icon name="api" />
</q-item-section>
<q-input
dense
prefix="开启快捷命令服务"
suffix="端口"
outlined
v-model="quickFeatures.apiServer.port"
input-class="text-center"
style="width: 280px"
type="text"
>
<template v-slot:append>
<q-toggle
@click="toggleFeature('apiServer')"
v-model="quickFeatures.apiServer.enable"
checked-icon="check"
color="primary"
/>
</template>
<q-tooltip
>启用后在主输入框输入快捷命令服务可以进入配置一个后台服务<br />
通过本地监听{{
quickFeatures.apiServer.port
}}端口的形式接收用户传送过来的参数然后根据参数执行不同的操作
<br />
本功能的意义在于 utools
的接口暴露出来可以通过命令行等外部途径 <br />
直接启用 ubrowser 或者直接redirect 到相应的插件<br />
需要配置插件跟随 utools 启动和保留后台
</q-tooltip>
</q-input>
</q-item>
</q-list>
</q-menu>
</q-item>
<!-- 导入 -->
<q-item clickable>
<q-item-section side>
@ -77,108 +247,6 @@
</q-list>
</q-menu>
</q-item>
<!-- 实用功能 -->
<q-item clickable>
<q-item-section side>
<q-icon name="keyboard_arrow_left" />
</q-item-section>
<q-item-section>实用功能</q-item-section>
<q-menu anchor="top end" self="top start">
<q-list>
<q-item>
<q-item-section side>
<q-icon name="folder_special" />
</q-item-section>
<q-input
dense
prefix="快速收藏文件至"
suffix="标签"
outlined
input-class="text-center"
style="width: 280px"
autofocus
v-model="quickFeatures.favFile.tag"
type="text"
>
<template v-slot:append>
<q-toggle
@click="toggleFeature('favFile')"
v-model="quickFeatures.favFile.enable"
checked-icon="check"
color="primary"
/>
</template>
<q-tooltip
>启用后选中文件可以通过超级面板快速将文件收藏到{{
quickFeatures.favFile.tag
}}标签
</q-tooltip>
</q-input>
</q-item>
<q-item>
<q-item-section side>
<q-icon name="bookmarks" />
</q-item-section>
<q-input
dense
prefix="快速收藏网址至"
suffix="标签"
outlined
input-class="text-center"
style="width: 280px"
autofocus
v-model="quickFeatures.favUrl.tag"
type="text"
>
<template v-slot:append>
<q-toggle
@click="toggleFeature('favUrl')"
v-model="quickFeatures.favUrl.enable"
checked-icon="check"
color="primary"
/>
</template>
<q-tooltip
>启用后在浏览器界面可以通过超级面板快速将网址收藏到{{
quickFeatures.favUrl.tag
}}标签
</q-tooltip>
</q-input>
</q-item>
<q-item>
<q-item-section side>
<q-icon name="drive_file_rename_outline" />
</q-item-section>
<q-input
dense
prefix="新建插件别名至"
suffix="标签"
outlined
input-class="text-center"
style="width: 280px"
autofocus
v-model="quickFeatures.pluNickName.tag"
type="text"
>
<template v-slot:append>
<q-toggle
@click="toggleFeature('pluNickName')"
v-model="quickFeatures.pluNickName.enable"
checked-icon="check"
color="primary"
/>
</template>
<q-tooltip
>启用后在主输入框输入插件别名可以快速设置插件别名<br />
并将所有设置的别名保存至{{
quickFeatures.pluNickName.tag
}}标签
</q-tooltip>
</q-input>
</q-item>
</q-list>
</q-menu>
</q-item>
<!-- 选项 -->
<q-item clickable>
<q-item-section side>

View File

@ -1,7 +1,7 @@
<template>
<div>
<div id="monacoEditor" style="width: 100%; height: 100%"></div>
<div class="absolute-center flex" v-show="!value">
<div class="absolute-center flex" v-show="!value && placeholder">
<div class="placeholder text-center q-gutter-md">
<div v-for="shortCut in shortCuts" :key="shortCut">
<span>{{ shortCut[0] }}</span
@ -56,6 +56,9 @@ export default {
return this.$q.dark.isActive;
},
},
props: {
placeholder: Boolean,
},
methods: {
initEditor() {
let monacoEditorPreferences = {

View File

@ -0,0 +1,127 @@
<template>
<div>
<MonacoEditor
:placeholder="false"
class="absolute-top"
ref="editor"
@typing="
(val) => {
$profile.quickFeatures.apiServer.cmd = val;
serverStatus && (needRestart = true);
}
"
:style="{
bottom: bottomHeight + 'px',
}"
/>
<div
class="
absolute-bottom
flex
items-center
justify-between
q-px-md
shadow-10
"
:style="{
height: bottomHeight + 'px',
}"
>
<div class="q-gutter-xs">
<q-badge color="primary" dense square>POST</q-badge
><q-badge color="primary" dense square>GET</q-badge>
<span>
http://127.0.0.1:{{ $profile.quickFeatures.apiServer.port }}
</span>
<span>的参数均会作为本脚本里的变量 </span>
</div>
<q-btn-group unelevated>
<q-btn flat color="primary" icon="help" />
<q-separator inset vertical />
<q-btn
flat
color="negative"
icon="stop"
v-if="serverStatus && !needRestart"
@click="stopServer"
label="停止服务"
/>
<q-btn
flat
color="warning"
icon="restart_alt"
v-else-if="serverStatus && needRestart"
@click="restartServer"
label="重启服务"
/>
<q-btn
flat
color="primary"
icon="play_arrow"
v-else
label="开启服务"
@click="enableServer"
>
</q-btn>
</q-btn-group>
</div>
</div>
</template>
<script>
import MonacoEditor from "components/MonacoEditor";
export default {
components: { MonacoEditor },
data() {
return {
bottomHeight: 40,
serverStatus: this.$profile.quickFeatures.apiServer.serverStatus,
needRestart: null,
};
},
mounted() {
this.$refs.editor.setEditorValue(this.$profile.quickFeatures.apiServer.cmd);
this.$refs.editor.setEditorLanguage("javascript");
this.needRestart = false;
},
methods: {
enableServer() {
quickcommand
.showConfirmBox(
"请注意,该接口未做任何权限鉴定,千万不要试图将本端口转发出去,否则无异于将本机的 shell 权限暴露在公网!",
"FBI WARNING"
)
.then((ok) => {
if (!ok) return;
this.$profile.quickFeatures.apiServer.serverStatus =
this.serverStatus = true;
window
.quickcommandHttpServer()
.run(
this.$profile.quickFeatures.apiServer.cmd,
this.$profile.quickFeatures.apiServer.port
);
quickcommand.showMessageBox("启动服务成功!");
});
},
stopServer() {
window.quickcommandHttpServer().stop();
this.$profile.quickFeatures.apiServer.serverStatus =
this.serverStatus = false;
quickcommand.showMessageBox("关闭服务成功!");
},
restartServer() {
window.quickcommandHttpServer().stop();
window
.quickcommandHttpServer()
.run(
this.$profile.quickFeatures.apiServer.cmd,
this.$profile.quickFeatures.apiServer.port
);
this.needRestart = false;
quickcommand.showMessageBox("重启服务成功!");
},
},
};
</script>

View File

@ -0,0 +1,55 @@
<template>
<q-card>
<q-card-section class="text-h5 text-center">定时执行</q-card-section>
<q-card-section class="q-gutter-md">
<q-input
v-model="crontab"
type="text"
standout="bg-primary text-white"
label="请输入 Crontab 表达式"
mask="# # # # # #"
>
<template v-slot:append>
<q-btn flat icon="help"></q-btn>
</template>
</q-input>
<pre class="explain">
{{ explain }}
</pre>
<div>当前状态</div>
<div>下次执行时间</div>
</q-card-section>
<q-card-actions align="right">
<q-btn flat color="negative" v-close-popup>禁用</q-btn>
<q-btn flat color="primary" v-close-popup>启用</q-btn>
</q-card-actions>
</q-card>
</template>
<script>
const explain = `
(可选) (0 - 59)
分钟 (0 - 59)
小时 (0 - 23)
(1 - 31)
(1 - 12, 一月-十二月)
星期 (0 - 6, 周日-周六)
* * * * * *`;
export default {
data() {
return {
crontab: "",
explain: explain,
};
},
};
</script>
<style scoped>
.explain {
background: #ffffff18;
padding: 0 10px;
border-radius: 5px;
}
</style>

View File

@ -0,0 +1,40 @@
<script>
export default {
mounted() {
utools.setExpendHeight(0);
quickcommand.enterData.payload.forEach((file) => {
let uid = this.getUid();
let fileInfo = window.getFileInfo({
type: "file",
argvs: file.path,
readfile: false,
});
let fileName = fileInfo.name.replace(fileInfo.ext, "");
let command = {
features: {
cmds: [fileName],
explain: `打开${fileName}`,
icon: utools.getFileIcon(file.path),
platform: [window.processPlatform],
code: `key_${uid}`,
},
program: "quickcommand",
cmd: `open(\"${file.path.replace(/\\/g, "\\\\")}\")`,
output: "ignore",
tags: [this.$profile.quickFeatures.favFile.tag],
};
this.importCommand(command);
});
utools.showNotification("操作成功!");
utools.outPlugin();
},
methods: {
getUid() {
return this.$parent.getUid();
},
importCommand(command) {
this.$parent.importCommand(command);
},
},
};
</script>

View File

@ -0,0 +1,76 @@
<script>
export default {
data() {
return {
cmdCtrlKey: window.processPlatform === "darwin" ? "command" : "control",
};
},
async mounted() {
utools.setExpendHeight(0);
utools.hideMainWindow();
// getCurrentBrowserUrl
// let url = utools.getCurrentBrowserUrl();
utools.simulateKeyboardTap("l", this.cmdCtrlKey);
await this.wait(50);
utools.simulateKeyboardTap("c", this.cmdCtrlKey);
await this.wait(50);
let url = window.clipboardReadText();
if (!/^http/.test(url)) {
utools.showMainWindow();
utools.setExpendHeight(550);
let choise = await quickcommand.showButtonBox(
["http", "https"],
"当前浏览器网址显示不完整,请问访问的页面是哪一种?"
);
url = choise.text + "://" + url;
}
let title = quickcommand.enterData.payload.title
.replace(/和另外 \d+ 个页面.*/, "")
.replace(/[-|—] .*?[Edge|Firefox|Chrome].*/, "")
.trim();
// let req = await axios(url)
// let title = quickcommand.htmlParse(req.data).querySelector('title').innerText
let base = /(http(s){0,1}:\/\/.*?(:\d+){0,1})(\/|$).*/.exec(url)[1];
let iconUrl = base + "/favicon.ico";
let iconPath = window.joinPath(
utools.getPath("temp"),
"quickcommandfavicon.ico"
);
let uid = this.getUid();
let command = {
features: {
explain: `访问${title}`,
cmds: [title],
platform: ["linux", "win32", "darwin"],
code: `key_${uid}`,
},
program: "quickcommand",
cmd: `visit(\"${url}\")\n`,
output: "ignore",
tags: [this.$profile.quickFeatures.favUrl.tag],
};
try {
let res = await quickcommand.downloadFile(iconUrl, iconPath);
if (res) command.features.icon = iconPath;
} catch (e) {}
this.importCommand(command);
utools.showNotification("操作成功!");
utools.outPlugin();
},
methods: {
wait(ms) {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, ms);
});
},
getUid() {
return this.$parent.getUid();
},
importCommand(command) {
this.$parent.importCommand(command);
},
},
};
</script>

View File

@ -0,0 +1,144 @@
<template>
<div class="flex justify-center content-center" style="height: 500px">
<div class="q-gutter-lg q-pa-lg" style="width: 600px">
<div class="text-center text-h2 q-ma-none">插件别名设置</div>
<q-select
outlined
transition-show="jump-down"
transition-hide="jump-up"
v-model="plugin"
:options="plugins"
type="text"
class="full-width"
:display-value="plugin.pluginName"
@update:model-value="feature = features[0]"
label="请选择插件"
>
<template v-slot:prepend>
<q-icon name="extension" />
</template>
<template v-slot:option="scope">
<q-item v-bind="scope.itemProps">
<q-item-section avatar>
<q-img :src="`file:///${scope.opt.logoPath}`" />
</q-item-section>
<q-item-section>
<q-item-label v-html="scope.opt.pluginName" />
<q-item-label caption>{{ scope.opt.description }}</q-item-label>
</q-item-section>
</q-item>
</template>
</q-select>
<q-select
outlined
transition-show="jump-down"
transition-hide="jump-up"
v-model="feature"
:options="features"
type="text"
:display-value="feature.cmd || ''"
class="full-width"
label="请选择功能关键字"
>
<template v-slot:prepend>
<q-icon name="font_download" />
</template>
<template v-slot:option="scope">
<q-item v-bind="scope.itemProps">
<q-item-section avatar>
<q-img :src="`file:///${plugin.logoPath}`" />
</q-item-section>
<q-item-section>
<q-item-label v-html="scope.opt.cmd" />
<q-item-label caption>{{ scope.opt.explain }}</q-item-label>
</q-item-section>
</q-item>
</template>
</q-select>
<q-select
class="full-width"
max-values="3"
type="text"
placeholder="键入后回车"
use-input
hide-dropdown-icon
use-chips
multiple
new-value-mode="add-unique"
input-debounce="0"
outlined
v-model="nickName"
label="要设置的别名"
>
<template v-slot:prepend>
<q-icon name="drive_file_rename_outline" />
</template>
</q-select>
<q-btn
class="full-width"
color="primary"
label="确定"
@click="addPluNickName()"
/>
</div>
</div>
</template>
<script>
export default {
data() {
return {
plugins: [],
plugin: {},
feature: {},
nickName: [],
};
},
mounted() {
this.plugins = _.values(window.getUtoolsPlugins());
this.plugin = this.plugins[0];
this.feature = this.features[0];
},
computed: {
features() {
return this.plugin?.features
?.map((x) => {
return {
explain: x.explain,
cmd: x.cmds.filter((y) => y.length)[0],
};
})
.filter((x) => x.cmd);
},
},
methods: {
addPluNickName() {
if (!this.nickName.length)
return quickcommand.showMessageBox("请填写别名", "warning");
let uid = this.getUid();
let command = {
features: {
cmds: this.nickName,
explain: this.feature.explain,
icon: window.getBase64Ico(this.plugin.logoPath),
platform: this.plugin.platform || ["darwin", "win32", "linux"],
code: `key_${uid}`,
},
program: "quickcommand",
cmd: `utools.redirect("${this.feature.cmd}");utools.showMainWindow()`,
output: "ignore",
tags: [this.$profile.quickFeatures.pluNickName.tag],
};
this.importCommand(command);
this.nickName = [];
quickcommand.showMessageBox("添加成功!");
},
getUid() {
return this.$parent.getUid();
},
importCommand(command) {
this.$parent.importCommand(command);
},
},
};
</script>

View File

@ -17,5 +17,15 @@ export default {
enable: false,
tag: "别名"
},
crontab: {
enable: false,
tag: "任务"
},
apiServer: {
enable: false,
port: 33442,
cmd: "",
serverStatus: false
}
}
}

View File

@ -42,7 +42,21 @@ const quickFeatures = {
cmds: ["插件别名"],
icon: require("../../assets/feature/plugin.png"),
platform: ["win32", "darwin", "linux"],
},
crontab: {
code: "feature_crontab",
explain: "为快捷命令添加计划任务",
cmds: ["计划任务", "crontab"],
icon: require("../../assets/feature/crontab.png"),
platform: ["win32", "darwin", "linux"],
},
apiServer: {
code: "feature_apiServer",
explain: "配置快捷命令后台服务",
cmds: ["快捷命令服务配置", "quickcommandServer"],
icon: require("../../assets/feature/api.png"),
platform: ["win32", "darwin", "linux"],
}
}
export default quickFeatures
export default quickFeatures

View File

@ -1,244 +1,29 @@
<template>
<div
class="flex justify-center content-center"
style="height: 500px"
v-show="showPluNickNameSetting"
>
<div class="q-gutter-lg q-pa-lg" style="width: 600px">
<div class="text-center text-h2 q-ma-none">插件别名设置</div>
<q-select
outlined
transition-show="jump-down"
transition-hide="jump-up"
v-model="plugin"
:options="plugins"
type="text"
class="full-width"
:display-value="plugin.pluginName"
@update:model-value="feature = features[0]"
label="请选择插件"
>
<template v-slot:prepend>
<q-icon name="extension" />
</template>
<template v-slot:option="scope">
<q-item v-bind="scope.itemProps">
<q-item-section avatar>
<q-img :src="`file:///${scope.opt.logoPath}`" />
</q-item-section>
<q-item-section>
<q-item-label v-html="scope.opt.pluginName" />
<q-item-label caption>{{ scope.opt.description }}</q-item-label>
</q-item-section>
</q-item>
</template>
</q-select>
<q-select
outlined
transition-show="jump-down"
transition-hide="jump-up"
v-model="feature"
:options="features"
type="text"
:display-value="feature.cmd || ''"
class="full-width"
label="请选择功能关键字"
>
<template v-slot:prepend>
<q-icon name="font_download" />
</template>
<template v-slot:option="scope">
<q-item v-bind="scope.itemProps">
<q-item-section avatar>
<q-img :src="`file:///${plugin.logoPath}`" />
</q-item-section>
<q-item-section>
<q-item-label v-html="scope.opt.cmd" />
<q-item-label caption>{{ scope.opt.explain }}</q-item-label>
</q-item-section>
</q-item>
</template>
</q-select>
<q-select
class="full-width"
max-values="3"
type="text"
placeholder="键入后回车"
use-input
hide-dropdown-icon
use-chips
multiple
new-value-mode="add-unique"
input-debounce="0"
outlined
v-model="nickName"
label="要设置的别名"
>
<template v-slot:prepend>
<q-icon name="drive_file_rename_outline" />
</template>
</q-select>
<q-btn
class="full-width"
color="primary"
label="确定"
@click="addPluNickName()"
/>
</div>
</div>
<component :is="currentComponent" :ref="currentComponent" />
</template>
<script>
import PluginNickName from "components/quickFeatures/PluginNickName";
import CrontabCmd from "components/quickFeatures/CrontabCmd";
import ApiServer from "components/quickFeatures/ApiServer";
import FavFile from "components/quickFeatures/FavFile";
import FavUrl from "components/quickFeatures/FavUrl";
import { markRaw } from "vue";
export default {
components: {
pluNickName: markRaw(PluginNickName),
crontab: markRaw(CrontabCmd),
apiServer: markRaw(ApiServer),
favFile: markRaw(FavFile),
favUrl: markRaw(FavUrl),
},
data() {
return {
featureType: this.$route.params.featuretype,
cmdCtrlKey: window.processPlatform === "darwin" ? "command" : "control",
plugins: [],
plugin: {},
feature: {},
nickName: [],
showPluNickNameSetting: false,
currentComponent: this.$route.params.featuretype,
};
},
computed: {
features() {
return this.plugin?.features
?.map((x) => {
return {
explain: x.explain,
cmd: x.cmds.filter((y) => y.length)[0],
};
})
.filter((x) => x.cmd);
},
},
mounted() {
switch (this.featureType) {
case "pluNickName":
this.showPluNickNameSetting = true;
this.plugins = _.values(window.getUtoolsPlugins());
this.plugin = this.plugins[0];
this.feature = this.features[0];
break;
case "favFile":
this.addFavFile();
break;
case "favUrl":
this.addFavUrl();
break;
default:
break;
}
},
methods: {
wait(ms) {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, ms);
});
},
getPlugins() {},
addPluNickName() {
if (!this.nickName.length)
return quickcommand.showMessageBox("请填写别名", "warning");
let uid = this.getUid();
let command = {
features: {
cmds: this.nickName,
explain: this.feature.explain,
icon: window.getBase64Ico(this.plugin.logoPath),
platform: this.plugin.platform || ["darwin", "win32", "linux"],
code: `key_${uid}`,
},
program: "quickcommand",
cmd: `utools.redirect("${this.feature.cmd}");utools.showMainWindow()`,
output: "ignore",
tags: [this.$profile.quickFeatures.pluNickName.tag],
};
this.importCommand(command);
this.nickName = [];
quickcommand.showMessageBox("添加成功!");
},
addFavFile() {
utools.setExpendHeight(0);
quickcommand.enterData.payload.forEach((file) => {
let uid = this.getUid();
let fileInfo = window.getFileInfo({
type: "file",
argvs: file.path,
readfile: false,
});
let fileName = fileInfo.name.replace(fileInfo.ext, "");
let command = {
features: {
cmds: [fileName],
explain: `打开${fileName}`,
icon: utools.getFileIcon(file.path),
platform: [window.processPlatform],
code: `key_${uid}`,
},
program: "quickcommand",
cmd: `open(\"${file.path.replace(/\\/g, "\\\\")}\")`,
output: "ignore",
tags: [this.$profile.quickFeatures.favFile.tag],
};
this.importCommand(command);
});
this.showMsg();
},
async addFavUrl() {
utools.setExpendHeight(0);
utools.hideMainWindow();
// getCurrentBrowserUrl
// let url = utools.getCurrentBrowserUrl();
utools.simulateKeyboardTap("l", this.cmdCtrlKey);
await this.wait(50);
utools.simulateKeyboardTap("c", this.cmdCtrlKey);
await this.wait(50);
let url = window.clipboardReadText();
if (!/^http/.test(url)) {
utools.showMainWindow();
utools.setExpendHeight(550);
let choise = await quickcommand.showButtonBox(
["http", "https"],
"当前浏览器网址显示不完整,请问访问的页面是哪一种?"
);
url = choise.text + "://" + url;
}
let title = quickcommand.enterData.payload.title
.replace(/和另外 \d+ 个页面.*/, "")
.replace(/[-|—] .*?[Edge|Firefox|Chrome].*/, "")
.trim();
// let req = await axios(url)
// let title = quickcommand.htmlParse(req.data).querySelector('title').innerText
let base = /(http(s){0,1}:\/\/.*?(:\d+){0,1})(\/|$).*/.exec(url)[1];
let iconUrl = base + "/favicon.ico";
let iconPath = window.joinPath(
utools.getPath("temp"),
"quickcommandfavicon.ico"
);
let uid = this.getUid();
let command = {
features: {
explain: `访问${title}`,
cmds: [title],
platform: ["linux", "win32", "darwin"],
code: `key_${uid}`,
},
program: "quickcommand",
cmd: `visit(\"${url}\")\n`,
output: "ignore",
tags: [this.$profile.quickFeatures.favUrl.tag],
};
try {
let res = await quickcommand.downloadFile(iconUrl, iconPath);
if (res) command.features.icon = iconPath;
} catch (e) {}
this.importCommand(command);
this.showMsg();
},
importCommand(command) {
command = _.cloneDeep(command);
this.$utools.putDB(
@ -252,10 +37,6 @@ export default {
Math.random().toString().substr(3, 3) + Date.now()
).toString(36);
},
showMsg() {
utools.showNotification("操作成功!");
utools.outPlugin();
},
},
};
</script>