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

<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>