mirror of
https://github.com/fofolee/uTools-quickcommand.git
synced 2025-08-01 00:49:50 +08:00
定时任务 80%
This commit is contained in:
parent
0547e65de1
commit
05999db5d2
@ -498,6 +498,15 @@ let createNodeVM = (userVars) => {
|
|||||||
return vm
|
return vm
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.showHelpPage = path => {
|
||||||
|
utools.ubrowser
|
||||||
|
.goto("https://www.yuque.com/fofolee-awga0/cpbg1m/bg31vl" + path)
|
||||||
|
.run({
|
||||||
|
width: 1380,
|
||||||
|
height: 750
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
window.VmEval = (cmd, sandbox = {}) => new VM({
|
window.VmEval = (cmd, sandbox = {}) => new VM({
|
||||||
sandbox: sandbox
|
sandbox: sandbox
|
||||||
}).run(cmd)
|
}).run(cmd)
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 3.7 KiB |
@ -37,10 +37,10 @@
|
|||||||
flat
|
flat
|
||||||
round
|
round
|
||||||
color="blue-9"
|
color="blue-9"
|
||||||
icon="share"
|
icon="insights"
|
||||||
>
|
>
|
||||||
<q-tooltip anchor="top middle" self="center middle">
|
<q-tooltip anchor="top middle" self="center middle">
|
||||||
导出
|
设置
|
||||||
</q-tooltip>
|
</q-tooltip>
|
||||||
<q-menu transition-show="jump-down" transition-hide="jump-up">
|
<q-menu transition-show="jump-down" transition-hide="jump-up">
|
||||||
<q-list style="min-width: 100px">
|
<q-list style="min-width: 100px">
|
||||||
@ -56,6 +56,34 @@
|
|||||||
</q-item-section>
|
</q-item-section>
|
||||||
<q-item-section>复制到剪贴板</q-item-section>
|
<q-item-section>复制到剪贴板</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
|
<q-item clickable disable>
|
||||||
|
<q-item-section side>
|
||||||
|
<q-icon name="share" />
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section>分享</q-item-section>
|
||||||
|
<q-tooltip>暂未开放</q-tooltip>
|
||||||
|
</q-item>
|
||||||
|
<q-item clickable disable>
|
||||||
|
<q-item-section side>
|
||||||
|
<q-icon name="supervisor_account" />
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section>分享设置</q-item-section>
|
||||||
|
<q-tooltip>暂未开放</q-tooltip>
|
||||||
|
</q-item>
|
||||||
|
<q-item
|
||||||
|
clickable
|
||||||
|
v-close-popup
|
||||||
|
@click="showCrontab = true"
|
||||||
|
:disable="!canCommandAddCron"
|
||||||
|
>
|
||||||
|
<q-item-section side>
|
||||||
|
<q-icon name="timer" />
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section>添加定时任务</q-item-section>
|
||||||
|
<q-tooltip
|
||||||
|
>定时执行当前命令,仅匹配类型为关键字时可用</q-tooltip
|
||||||
|
>
|
||||||
|
</q-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
</q-menu>
|
</q-menu>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
@ -201,20 +229,32 @@
|
|||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card>
|
</q-card>
|
||||||
</div>
|
</div>
|
||||||
|
<q-dialog v-model="showCrontab">
|
||||||
|
<CrontabSetting
|
||||||
|
:cronStatus="$profile.crontabs[featureCode]?.status"
|
||||||
|
:cronExp="$profile.crontabs[featureCode]?.cronExp"
|
||||||
|
@addCrontab="addCrontab"
|
||||||
|
@delCrontab="delCrontab"
|
||||||
|
/>
|
||||||
|
</q-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import commandTypes from "../js/options/commandTypes.js";
|
import commandTypes from "../js/options/commandTypes.js";
|
||||||
import platformTypes from "../js/options/platformTypes.js";
|
import platformTypes from "../js/options/platformTypes.js";
|
||||||
|
import CrontabSetting from "components/popup/CrontabSetting";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
components: { CrontabSetting },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
allProgrammings: this.$programmings,
|
allProgrammings: this.$programmings,
|
||||||
maxCmdStingLen: 8,
|
maxCmdStingLen: 8,
|
||||||
commandTypes: commandTypes,
|
commandTypes: commandTypes,
|
||||||
platformTypes: platformTypes,
|
platformTypes: platformTypes,
|
||||||
|
showCrontab: false,
|
||||||
|
cronJob: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -243,6 +283,10 @@ export default {
|
|||||||
if (cmd.type && cmd.type === "window") return false;
|
if (cmd.type && cmd.type === "window") return false;
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
// 命令未启用也可以添加计划任务
|
||||||
|
canCommandAddCron() {
|
||||||
|
return !!this.commandInfo.features.cmds[0].length;
|
||||||
|
},
|
||||||
// 默认命令不可删除
|
// 默认命令不可删除
|
||||||
canCommandEdit() {
|
canCommandEdit() {
|
||||||
if (utools.isDev()) return true;
|
if (utools.isDev()) return true;
|
||||||
@ -267,6 +311,9 @@ export default {
|
|||||||
cardBgColor() {
|
cardBgColor() {
|
||||||
return this.$q.dark.isActive ? "#ffffff08" : "#00000008";
|
return this.$q.dark.isActive ? "#ffffff08" : "#00000008";
|
||||||
},
|
},
|
||||||
|
featureCode() {
|
||||||
|
return this.commandInfo.features.code;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
commandInfo: Object,
|
commandInfo: Object,
|
||||||
@ -298,7 +345,7 @@ export default {
|
|||||||
editCommand() {
|
editCommand() {
|
||||||
let event = {
|
let event = {
|
||||||
type: "edit",
|
type: "edit",
|
||||||
data: this.commandInfo.features.code,
|
data: this.featureCode,
|
||||||
};
|
};
|
||||||
this.$emit("commandChanged", event);
|
this.$emit("commandChanged", event);
|
||||||
},
|
},
|
||||||
@ -306,14 +353,29 @@ export default {
|
|||||||
runCommand() {
|
runCommand() {
|
||||||
let event = {
|
let event = {
|
||||||
type: "run",
|
type: "run",
|
||||||
data: this.commandInfo.features.code,
|
data: this.featureCode,
|
||||||
};
|
};
|
||||||
this.$emit("commandChanged", event);
|
this.$emit("commandChanged", event);
|
||||||
},
|
},
|
||||||
|
addCrontab(cronExp) {
|
||||||
|
this.$profile.crontabs[this.featureCode] = {
|
||||||
|
cronStatus: true,
|
||||||
|
cronExp: cronExp,
|
||||||
|
};
|
||||||
|
this.cronJob = this.$Cron(cronExp, () => {
|
||||||
|
this.runCommand();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
delCrontab() {
|
||||||
|
this.$profile.crontabs[this.featureCode] = {
|
||||||
|
cronStatus: false,
|
||||||
|
};
|
||||||
|
this.cronJob.stop();
|
||||||
|
},
|
||||||
// 启用/禁用命令
|
// 启用/禁用命令
|
||||||
toggleCommandActivated() {
|
toggleCommandActivated() {
|
||||||
let event = {
|
let event = {
|
||||||
data: this.commandInfo.features.code,
|
data: this.featureCode,
|
||||||
type: "toggle",
|
type: "toggle",
|
||||||
};
|
};
|
||||||
event.type = this.isCommandActivated ? "disable" : "enable";
|
event.type = this.isCommandActivated ? "disable" : "enable";
|
||||||
@ -324,7 +386,7 @@ export default {
|
|||||||
quickcommand.showConfirmBox("删除这个快捷命令").then((x) => {
|
quickcommand.showConfirmBox("删除这个快捷命令").then((x) => {
|
||||||
if (!x) return;
|
if (!x) return;
|
||||||
this.isCommandAlive = false;
|
this.isCommandAlive = false;
|
||||||
let code = this.commandInfo.features.code;
|
let code = this.featureCode;
|
||||||
this.$emit("commandChanged", {
|
this.$emit("commandChanged", {
|
||||||
type: "remove",
|
type: "remove",
|
||||||
data: code,
|
data: code,
|
||||||
|
@ -110,40 +110,6 @@
|
|||||||
</q-tooltip>
|
</q-tooltip>
|
||||||
</q-input>
|
</q-input>
|
||||||
</q-item>
|
</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>
|
||||||
<q-item-section side>
|
<q-item-section side>
|
||||||
<q-icon name="api" />
|
<q-icon name="api" />
|
||||||
|
161
src/components/popup/CrontabSetting.vue
Normal file
161
src/components/popup/CrontabSetting.vue
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
<template>
|
||||||
|
<q-card>
|
||||||
|
<q-card-section class="text-h5 text-center">定时执行</q-card-section>
|
||||||
|
<q-card-section class="flex">
|
||||||
|
<div
|
||||||
|
v-for="item in Object.keys(cronDetail)"
|
||||||
|
:key="item"
|
||||||
|
class="q-pa-sm"
|
||||||
|
:style="{
|
||||||
|
width: '20%',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<q-input
|
||||||
|
v-model="cronDetail[item].value"
|
||||||
|
stack-label
|
||||||
|
autofocus
|
||||||
|
type="text"
|
||||||
|
standout="bg-primary text-white"
|
||||||
|
:label="cronDetail[item].label"
|
||||||
|
>
|
||||||
|
</q-input>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
<q-card-section class="q-px-lg q-gutter-md">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="text-primary text-weight-bolder">表达式</div>
|
||||||
|
<div class="q-px-sm">
|
||||||
|
{{ cronConverted }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex">
|
||||||
|
<div class="text-primary text-weight-bolder">距下次</div>
|
||||||
|
<div class="q-px-sm">
|
||||||
|
{{ nextLeftTime }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex">
|
||||||
|
<div class="text-primary text-weight-bolder">下三次</div>
|
||||||
|
<div v-for="time in nextTasks" :key="time" class="q-px-sm">
|
||||||
|
{{ time }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
<q-card-actions align="right" class="q-px-lg">
|
||||||
|
<q-btn
|
||||||
|
flat
|
||||||
|
color="grey"
|
||||||
|
icon="arrow_back_ios_new"
|
||||||
|
label="返回"
|
||||||
|
v-close-popup
|
||||||
|
/>
|
||||||
|
<q-btn flat color="info" icon="help" label="帮助" @click="showHelp" />
|
||||||
|
<q-btn
|
||||||
|
flat
|
||||||
|
color="negative"
|
||||||
|
icon="alarm_off"
|
||||||
|
v-if="cronStatus"
|
||||||
|
@click="delCrontab"
|
||||||
|
label="禁用"
|
||||||
|
/>
|
||||||
|
<q-btn
|
||||||
|
flat
|
||||||
|
color="primary"
|
||||||
|
icon="alarm_on"
|
||||||
|
v-else
|
||||||
|
@click="addCrontab"
|
||||||
|
label="启用"
|
||||||
|
/>
|
||||||
|
</q-card-actions>
|
||||||
|
</q-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
cronDetail: {
|
||||||
|
min: { label: "分钟", value: "" },
|
||||||
|
hour: { label: "小时", value: "" },
|
||||||
|
day: { label: "天", value: "*" },
|
||||||
|
month: { label: "月", value: "*" },
|
||||||
|
week: { label: "星期", value: "*" },
|
||||||
|
},
|
||||||
|
intervalId: null,
|
||||||
|
nextLeftTime: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
cronStatus: Boolean,
|
||||||
|
cronExp: String,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
cronConverted() {
|
||||||
|
return _.values(this.cronDetail)
|
||||||
|
.map((x) => x.value)
|
||||||
|
.join(" ");
|
||||||
|
},
|
||||||
|
cronJob() {
|
||||||
|
try {
|
||||||
|
return this.$Cron(this.cronConverted);
|
||||||
|
} catch (error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
nextTasks() {
|
||||||
|
return this.cronJob?.enumerate(3)?.map((x) => x?.toLocaleString());
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
formatDuring(ms) {
|
||||||
|
if (!ms) return;
|
||||||
|
let days = parseInt(ms / (1000 * 60 * 60 * 24));
|
||||||
|
let hours = parseInt((ms % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
|
||||||
|
let minutes = parseInt((ms % (1000 * 60 * 60)) / (1000 * 60));
|
||||||
|
let seconds = Math.floor((ms % (1000 * 60)) / 1000);
|
||||||
|
return (
|
||||||
|
days + " 天 " + hours + " 小时 " + minutes + " 分钟 " + seconds + " 秒 "
|
||||||
|
);
|
||||||
|
},
|
||||||
|
refreshTime() {
|
||||||
|
if (!!this.intervalId) return;
|
||||||
|
this.intervalId = setInterval(() => {
|
||||||
|
this.nextLeftTime = this.formatDuring(this.cronJob?.msToNext());
|
||||||
|
}, 1000);
|
||||||
|
},
|
||||||
|
showHelp() {
|
||||||
|
window.showHelpPage("#Q0e7s");
|
||||||
|
},
|
||||||
|
initValue() {
|
||||||
|
if (!this.cronExp) return;
|
||||||
|
let splited = this.cronExp.split(" ");
|
||||||
|
Object.keys(this.cronDetail).forEach((key, index) => {
|
||||||
|
this.cronDetail[key].value = splited[index];
|
||||||
|
});
|
||||||
|
},
|
||||||
|
addCrontab() {
|
||||||
|
if (!this.cronJob)
|
||||||
|
return quickcommand.showMessageBox(
|
||||||
|
"crontab表达式错误,请检查!",
|
||||||
|
"error"
|
||||||
|
);
|
||||||
|
this.$emit("addCrontab", this.cronConverted);
|
||||||
|
quickcommand.showMessageBox(
|
||||||
|
`启用成功,下次执行时间为 ${this.nextTasks[0]}`
|
||||||
|
);
|
||||||
|
},
|
||||||
|
delCrontab() {
|
||||||
|
this.$emit("delCrontab");
|
||||||
|
quickcommand.showMessageBox("禁用成功");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.initValue();
|
||||||
|
this.refreshTime();
|
||||||
|
},
|
||||||
|
unmounted() {
|
||||||
|
clearInterval(this.intervalId);
|
||||||
|
this.intervalId = null;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
@ -128,12 +128,7 @@ export default {
|
|||||||
}, 1000);
|
}, 1000);
|
||||||
},
|
},
|
||||||
showHelp() {
|
showHelp() {
|
||||||
utools.ubrowser
|
window.showHelpPage("#GNjEg");
|
||||||
.goto("https://www.yuque.com/fofolee-awga0/cpbg1m/bg31vl#GNjEg")
|
|
||||||
.run({
|
|
||||||
width: 1380,
|
|
||||||
height: 750,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,55 +0,0 @@
|
|||||||
<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>
|
|
@ -4,6 +4,7 @@ export default {
|
|||||||
defaultPrimaryColor: "#009688",
|
defaultPrimaryColor: "#009688",
|
||||||
backgroundImg: null,
|
backgroundImg: null,
|
||||||
codeHistory: {},
|
codeHistory: {},
|
||||||
|
crontabs: {},
|
||||||
quickFeatures: {
|
quickFeatures: {
|
||||||
favFile: {
|
favFile: {
|
||||||
enable: false,
|
enable: false,
|
||||||
@ -17,10 +18,6 @@ export default {
|
|||||||
enable: false,
|
enable: false,
|
||||||
tag: "别名"
|
tag: "别名"
|
||||||
},
|
},
|
||||||
crontab: {
|
|
||||||
enable: false,
|
|
||||||
tag: "任务"
|
|
||||||
},
|
|
||||||
apiServer: {
|
apiServer: {
|
||||||
enable: false,
|
enable: false,
|
||||||
port: 33442,
|
port: 33442,
|
||||||
|
@ -43,13 +43,6 @@ const quickFeatures = {
|
|||||||
icon: require("../../assets/feature/plugin.png"),
|
icon: require("../../assets/feature/plugin.png"),
|
||||||
platform: ["win32", "darwin", "linux"],
|
platform: ["win32", "darwin", "linux"],
|
||||||
},
|
},
|
||||||
crontab: {
|
|
||||||
code: "feature_crontab",
|
|
||||||
explain: "为快捷命令添加计划任务",
|
|
||||||
cmds: ["计划任务", "crontab"],
|
|
||||||
icon: require("../../assets/feature/crontab.png"),
|
|
||||||
platform: ["win32", "darwin", "linux"],
|
|
||||||
},
|
|
||||||
apiServer: {
|
apiServer: {
|
||||||
code: "feature_apiServer",
|
code: "feature_apiServer",
|
||||||
explain: "配置快捷命令后台服务",
|
explain: "配置快捷命令后台服务",
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import PluginNickName from "components/quickFeatures/PluginNickName";
|
import PluginNickName from "components/quickFeatures/PluginNickName";
|
||||||
import CrontabCmd from "components/quickFeatures/CrontabCmd";
|
|
||||||
import ApiServer from "components/quickFeatures/ApiServer";
|
import ApiServer from "components/quickFeatures/ApiServer";
|
||||||
import FavFile from "components/quickFeatures/FavFile";
|
import FavFile from "components/quickFeatures/FavFile";
|
||||||
import FavUrl from "components/quickFeatures/FavUrl";
|
import FavUrl from "components/quickFeatures/FavUrl";
|
||||||
@ -13,7 +12,6 @@ import { markRaw } from "vue";
|
|||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
pluNickName: markRaw(PluginNickName),
|
pluNickName: markRaw(PluginNickName),
|
||||||
crontab: markRaw(CrontabCmd),
|
|
||||||
apiServer: markRaw(ApiServer),
|
apiServer: markRaw(ApiServer),
|
||||||
favFile: markRaw(FavFile),
|
favFile: markRaw(FavFile),
|
||||||
favUrl: markRaw(FavUrl),
|
favUrl: markRaw(FavUrl),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user