You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
314 lines
9.2 KiB
314 lines
9.2 KiB
<template>
|
|
<div class="title">
|
|
<h3>
|
|
<component :is="renderIcon('antd CodeOutlined')" />
|
|
脚本编辑
|
|
</h3>
|
|
<el-row>
|
|
<el-divider direction="vertical" />
|
|
<el-button :icon="renderIcon('Save')" link
|
|
@click="saveScripts">保存
|
|
</el-button>
|
|
<el-button :icon="renderIcon('antd PullRequestOutlined')" link
|
|
@click="syncScripts">拉取
|
|
</el-button>
|
|
<el-divider direction="vertical" />
|
|
<el-button :icon="renderIcon('CirclePlus')" link
|
|
@click="addScript">添加
|
|
</el-button>
|
|
<el-button :icon="renderIcon('Delete')" link
|
|
:disabled="scriptList.length === 0 || !scriptList[scriptIndex]"
|
|
@click="removeCurrentScript">删除
|
|
</el-button>
|
|
<el-divider direction="vertical" />
|
|
<el-button :icon="renderIcon('antd PlayCircleOutlined')" link type="primary"
|
|
v-if="scriptList[scriptIndex] && !scriptList[scriptIndex].isRunning"
|
|
@click="()=>runScript(scriptList[scriptIndex])">运行脚本
|
|
</el-button>
|
|
<el-button :icon="renderIcon('antd StopOutlined')" link type="danger"
|
|
v-if="scriptList[scriptIndex] && scriptList[scriptIndex].isRunning"
|
|
@click="()=>stopScript(scriptList[scriptIndex])">停止运行
|
|
</el-button>
|
|
<el-button :icon="renderIcon('fa EyeDropper')" link
|
|
:disabled="scriptList.length === 0 || !scriptList[scriptIndex]"
|
|
@click="setCodeDropper">编码吸管
|
|
</el-button>
|
|
<el-divider direction="vertical" />
|
|
<el-radio-group v-model="scriptIndex" size="small">
|
|
<!-- 用 vue3-menus 在这里加一个右键,重命名-->
|
|
<el-radio-button v-for="(item, index) in scriptList" :key="index" :class="'script_'+index"
|
|
:label="item.name" :value="index" v-menus="scriptMenu" />
|
|
</el-radio-group>
|
|
<el-button link v-if="scriptList[scriptIndex]"
|
|
@click="()=>localRun(scriptList[scriptIndex])">LocalRun
|
|
</el-button>
|
|
</el-row>
|
|
<span class="close" @click="closeMe">
|
|
<component :is="renderIcon('element Close')"></component>
|
|
</span>
|
|
</div>
|
|
<div class="calc-bottom-panel">
|
|
<YvSrcEditor ref="jsEditor" language="typescript" :key="scriptIndex"
|
|
v-if="scriptList.length > 0 && scriptList[scriptIndex]"
|
|
v-model="currentScript" />
|
|
</div>
|
|
</template>
|
|
<script>
|
|
import localforage from 'localforage'
|
|
import YvSrcEditor from '@/components/YvSrcEditor.vue'
|
|
import IWidgets from '../IWidgets.js'
|
|
import CodeDropper from '@/core/manager/CodeDropper.js'
|
|
import { escByKeyboard } from '@/core/ModelUtils.js'
|
|
import scriptRunner from '@/runtime/ScriptRunner.js'
|
|
|
|
export default {
|
|
name: 'ScriptView',
|
|
components: {
|
|
YvSrcEditor
|
|
},
|
|
mixins: [IWidgets],
|
|
mounted() {
|
|
window['ScriptView'] = this
|
|
},
|
|
unmounted() {
|
|
window['ScriptView'] = null
|
|
},
|
|
data() {
|
|
const me = this
|
|
|
|
return {
|
|
scriptIsRunning: false,
|
|
scriptIndex: 0,
|
|
/**
|
|
* 脚本列表
|
|
* @type {Array<CustomScript>}
|
|
*/
|
|
scriptList: [],
|
|
searchKeyword: '',
|
|
scriptMenu: [
|
|
{
|
|
name: 'removeScript', label: '删除',
|
|
click(menu, { class: classJoinStr }) {
|
|
// 拿到 classJoinStr 最后一截字符串, 取到 scriptIndex
|
|
const index = parseInt(classJoinStr.split('_').pop())
|
|
if (isNaN(index) || index < 0 || index >= me.scriptList.length) {
|
|
system.msg('脚本索引错误', 'error')
|
|
return
|
|
}
|
|
const script = me.scriptList[index]
|
|
if (!script) {
|
|
system.msg('脚本不存在', 'error')
|
|
return
|
|
}
|
|
if (confirm(`确定要删除脚本 "${script.name}" 吗?`)) {
|
|
_.remove(me.scriptList, (item, idx) => idx === index)
|
|
if (me.scriptList.length === 0) {
|
|
me.scriptIndex = 0
|
|
} else if (me.scriptIndex >= me.scriptList.length) {
|
|
me.scriptIndex = me.scriptList.length - 1
|
|
}
|
|
}
|
|
}
|
|
},
|
|
{
|
|
name: 'rename', label: '重命名',
|
|
click(menu, { class: classJoinStr }) {
|
|
// 拿到 classJoinStr 最后一截字符串, 取到 scriptIndex
|
|
const index = parseInt(classJoinStr.split('_').pop())
|
|
if (isNaN(index) || index < 0 || index >= me.scriptList.length) {
|
|
system.msg('脚本索引错误', 'error')
|
|
return
|
|
}
|
|
const script = me.scriptList[index]
|
|
if (!script) {
|
|
system.msg('脚本不存在', 'error')
|
|
return
|
|
}
|
|
const newName = prompt('请输入新的脚本名称', script.name)
|
|
if (newName && newName.trim()) {
|
|
script.name = newName.trim()
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
},
|
|
watch: {
|
|
scriptList: {
|
|
handler(newVal) {
|
|
this.saveScriptsToLocalDebounce(this)
|
|
},
|
|
deep: true
|
|
}
|
|
},
|
|
created() {
|
|
this.loadFromLocal()
|
|
},
|
|
methods: {
|
|
async saveScripts() {
|
|
system.showLoading()
|
|
try {
|
|
await LCC.saveScripts(this.scriptList)
|
|
system.msg('脚本已保存')
|
|
|
|
} finally {
|
|
system.clearLoading()
|
|
}
|
|
},
|
|
async syncScripts() {
|
|
// 保存当前脚本索引
|
|
const originName = this.scriptList[this.scriptIndex]?.name
|
|
system.showLoading()
|
|
try {
|
|
const serverResponse = await LCC.syncScripts()
|
|
if (serverResponse.success) {
|
|
this.scriptList = serverResponse.data || []
|
|
|
|
// 从 originName 中找到对应的脚本索引
|
|
if (originName) {
|
|
const index = this.scriptList.findIndex(item => item.name === originName)
|
|
if (index >= 0) {
|
|
this.scriptIndex = index
|
|
return
|
|
}
|
|
}
|
|
|
|
this.scriptIndex = 0
|
|
}
|
|
|
|
system.msg('脚本已同步')
|
|
|
|
} finally {
|
|
system.clearLoading()
|
|
}
|
|
},
|
|
/**
|
|
* 运行脚本
|
|
* @type {(script: CustomScript) => void}
|
|
*/
|
|
runScript(script) {
|
|
system.showLoading()
|
|
scriptRunner.runScript(script)
|
|
.finally(() => {
|
|
system.clearLoading()
|
|
})
|
|
// this.viewport.modelManager.runScript(script)
|
|
},
|
|
|
|
/**
|
|
* 停止脚本
|
|
* @type {(script: CustomScript) => void}
|
|
*/
|
|
stopScript(script) {
|
|
scriptRunner.stopScript(script)
|
|
},
|
|
localRun(script) {
|
|
this.viewport.modelManager.executestring(script.content)
|
|
},
|
|
setCodeDropper() {
|
|
CodeDropper.start(this.viewport, (item) => {
|
|
if (item) {
|
|
if (!this.$refs.jsEditor) {
|
|
system.msg('代码编辑器未加载', 'warning')
|
|
return false
|
|
}
|
|
//@ts-ignore
|
|
this.$refs.jsEditor.insertText(`Model.find("${item.id}")`)
|
|
//@ts-ignore
|
|
this.$refs.jsEditor.focus()
|
|
return true
|
|
|
|
} else {
|
|
system.msg('代码吸管没有获取到内容', 'warning')
|
|
return false
|
|
}
|
|
})
|
|
},
|
|
saveScriptsToLocal() {
|
|
localforage.setItem('-yvan-lcc-scriptlist-', JSON.stringify({
|
|
scriptList: _.map(this.scriptList, item => {
|
|
return {
|
|
name: item.name,
|
|
content: item.content
|
|
}
|
|
}),
|
|
scriptIndex: this.scriptIndex
|
|
|
|
})).catch(err => {
|
|
system.msg('保存脚本到本地失败:', 'error')
|
|
})
|
|
},
|
|
saveScriptsToLocalDebounce: _.debounce(function(me) {
|
|
me.saveScriptsToLocal()
|
|
}, 1000),
|
|
loadFromLocal() {
|
|
localforage.getItem('-yvan-lcc-scriptlist-')
|
|
.then(value => {
|
|
if (value) {
|
|
const res = JSON.parse(value)
|
|
this.scriptList = res.scriptList || []
|
|
this.scriptIndex = res.scriptIndex || 0
|
|
} else {
|
|
this.scriptList = []
|
|
}
|
|
})
|
|
.catch(err => {
|
|
this.scriptList = []
|
|
system.msg('加载脚本失败:', 'error')
|
|
})
|
|
},
|
|
addScript() {
|
|
this.scriptList.push({
|
|
name: 'Script' + (this.scriptList.length + 1),
|
|
content: '',
|
|
isRunning: false
|
|
})
|
|
this.scriptIndex = this.scriptList.length - 1
|
|
},
|
|
removeCurrentScript() {
|
|
_.remove(this.scriptList, (item, index) => {
|
|
return index === this.scriptIndex
|
|
})
|
|
if (this.scriptList.length === 0) {
|
|
this.scriptIndex = 0
|
|
} else if (this.scriptIndex >= this.scriptList.length) {
|
|
this.scriptIndex = this.scriptList.length - 1
|
|
}
|
|
}
|
|
},
|
|
computed: {
|
|
currentScript: {
|
|
get() {
|
|
return this.scriptList[this.scriptIndex]?.content || ''
|
|
},
|
|
set(value) {
|
|
if (!this.scriptList[this.scriptIndex]) {
|
|
this.scriptList[this.scriptIndex] = {
|
|
name: 'Script' + (this.scriptList.length + 1),
|
|
content: '',
|
|
isRunning: false
|
|
}
|
|
}
|
|
this.scriptList[this.scriptIndex].content = value
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
<style lang="less">
|
|
.title {
|
|
& > h3 {
|
|
margin-right: 10px;
|
|
|
|
.el-icon {
|
|
position: relative;
|
|
top: 2px;
|
|
}
|
|
}
|
|
|
|
& > .el-row {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
}
|
|
</style>
|
|
|