chore: 插件编辑器

This commit is contained in:
xiaojunnuo
2025-04-06 18:06:21 +08:00
parent 61e322678b
commit 840a7b7c73
14 changed files with 731 additions and 305 deletions
@@ -6,7 +6,7 @@ export async function GetList(query: any) {
return await request({
url: apiPrefix + "/page",
method: "post",
data: query
data: query,
});
}
@@ -14,7 +14,7 @@ export async function AddObj(obj: any) {
return await request({
url: apiPrefix + "/add",
method: "post",
data: obj
data: obj,
});
}
@@ -22,7 +22,7 @@ export async function UpdateObj(obj: any) {
return await request({
url: apiPrefix + "/update",
method: "post",
data: obj
data: obj,
});
}
@@ -30,7 +30,7 @@ export async function DelObj(id: any) {
return await request({
url: apiPrefix + "/delete",
method: "post",
params: { id }
params: { id },
});
}
@@ -38,7 +38,7 @@ export async function GetObj(id: any) {
return await request({
url: apiPrefix + "/info",
method: "post",
params: { id }
params: { id },
});
}
@@ -46,7 +46,7 @@ export async function GetDetail(id: any) {
return await request({
url: apiPrefix + "/detail",
method: "post",
params: { id }
params: { id },
});
}
@@ -54,7 +54,7 @@ export async function DeleteBatch(ids: any[]) {
return await request({
url: apiPrefix + "/deleteByIds",
method: "post",
data: { ids }
data: { ids },
});
}
@@ -62,7 +62,7 @@ export async function SetDisabled(data: { id?: number; name?: string; type?: str
return await request({
url: apiPrefix + "/setDisabled",
method: "post",
data: data
data: data,
});
}
@@ -90,7 +90,7 @@ export type CommPluginConfig = {
export async function GetCommPluginConfigs(): Promise<CommPluginConfig> {
return await request({
url: apiPrefix + "/getCommPluginConfigs",
method: "post"
method: "post",
});
}
@@ -98,6 +98,6 @@ export async function SaveCommPluginConfigs(data: CommPluginConfig): Promise<voi
return await request({
url: apiPrefix + "/saveCommPluginConfigs",
method: "post",
data
data,
});
}
@@ -0,0 +1,77 @@
<template>
<div class="plugin-inputs">
<div class="actions mb-1">
<a-button type="primary" @click="addNewField"> 添加 </a-button>
</div>
<div class="inputs">
<div class="inputs-inner">
<a-collapse v-model:active-key="activeKey">
<a-collapse-panel v-for="(item, index) in inputs" :key="index">
<template #header> {{ item.key }} {{ item.title }} </template>
<a-form :label-col="{ style: { width: '80px' } }">
<a-form-item label="字段名称">
<a-input v-model:value="item.key" />
</a-form-item>
<a-form-item label="字段标题">
<a-input v-model:value="item.title" />
</a-form-item>
<a-form-item label="字段说明">
<a-input v-model:value="item.helper" />
</a-form-item>
<a-form-item label="默认值">
<a-input v-model:value="item.value" />
</a-form-item>
<a-form-item label="必填">
<a-switch v-model:checked="item.required" />
</a-form-item>
<a-form-item label="组件配置">
<fs-editor-code v-model:model-value="item.component" language="yaml" />
</a-form-item>
</a-form>
</a-collapse-panel>
</a-collapse>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, Ref } from "vue";
const activeKey = ref([]);
const inputs: Ref = ref([]);
function addNewField() {
inputs.value.push({
key: "newInput",
title: "新字段",
component: `
name: a-input
`,
helper: "",
value: undefined,
required: false,
});
}
</script>
<style lang="less">
.plugin-inputs {
display: flex;
flex-direction: column;
height: 100%;
.actions {
}
.inputs {
flex: 1;
height: 100%;
overflow: hidden;
.inputs-inner {
height: 100%;
overflow-y: auto;
}
}
}
</style>
@@ -2,7 +2,7 @@ import * as api from "./api";
import { useI18n } from "vue-i18n";
import { computed, Ref, ref } from "vue";
import { useRouter } from "vue-router";
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes, utils } from "@fast-crud/fast-crud";
import { AddReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes, utils } from "@fast-crud/fast-crud";
import { useUserStore } from "/src/store/modules/user";
import { useSettingStore } from "/src/store/modules/settings";
import { Modal } from "ant-design-vue";
@@ -59,29 +59,43 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
actionbar: {
buttons: {
add: {
show: false,
show: true,
text: "自定义插件",
},
},
},
rowHandle: {
show: false,
show: true,
minWidth: 200,
fixed: "right",
buttons: {
edit: {
show: false,
show: compute(({ row }) => {
return row.type === "custom";
}),
},
copy: {
show: false,
show: compute(({ row }) => {
return row.type === "custom";
}),
},
remove: {
show: false,
show: compute(({ row }) => {
return row.type === "custom";
}),
},
},
},
table: {
rowKey: "name",
},
tabs: {
name: "type",
show: true,
defaultOption: {
show: false,
},
},
columns: {
// id: {
// title: "ID",
@@ -101,20 +115,47 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
show: true,
},
form: {
show: false,
show: true,
order: 0,
helper: "必须为英文,驼峰命名,类型作为前缀\n例如AliyunDeployToCDN",
rules: [{ required: true }],
},
column: {
width: 250,
cellRender({ row }) {
if (row.author) {
return <fs-copyable model-value={`${row.author}/${row.name}`} />;
} else {
return <fs-copyable model-value={row.name} />;
}
},
},
},
author: {
title: "作者",
type: "text",
search: {
show: true,
},
form: {
show: true,
order: 0,
helper: "上传到应用商店时,将作为插件名称前缀,例如:greper/pluginName",
rules: [{ required: true }],
},
column: {
width: 200,
show: false,
},
},
icon: {
title: "图标",
type: "text",
type: "icon",
form: {
show: false,
rules: [{ required: true }],
},
column: {
width: 100,
width: 70,
align: "center",
component: {
name: "fs-icon",
@@ -125,26 +166,102 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
},
},
},
title: {
title: "标题",
type: "text",
form: {
order: 0,
helper: "插件中文名称",
rules: [{ required: true }],
},
column: {
width: 300,
cellRender({ row }) {
if (row.type === "custom") {
return <router-link to={`/sys/plugin/edit?id=${row.id}`}>{row.title}</router-link>;
}
return <div>{row.title}</div>;
},
},
},
desc: {
title: "描述",
type: "text",
helper: "插件的描述",
column: {
width: 300,
show: false,
},
},
type: {
title: "来源",
type: "dict-select",
search: {
show: true,
},
form: {
value: "custom",
component: {
disabled: true,
},
},
dict: dict({
data: [
{ label: "内置", value: "builtIn" },
{ label: "自建", value: "custom" },
{ label: "商店", value: "store" },
],
}),
column: {
width: 70,
align: "center",
component: {
color: "auto",
},
},
},
pluginType: {
title: "插件类型",
type: "dict-select",
search: {
show: true,
},
form: {
rules: [{ required: true }],
},
dict: dict({
data: [
{ label: "授权", value: "access" },
{ label: "DNS", value: "dnsProvider" },
{ label: "部署插件", value: "plugin" },
],
}),
column: {
width: 100,
align: "center",
component: {
color: "auto",
},
},
},
group: {
title: "分组",
type: "text",
type: "dict-select",
dict: dict({
url: "/pi/plugin/groupsList",
label: "title",
value: "key",
}),
form: {
rules: [{ required: true }],
},
column: {
width: 100,
align: "center",
align: "left",
component: {
color: "auto",
},
},
},
disabled: {
@@ -0,0 +1,110 @@
<template>
<fs-page class="page-plugin-edit">
<template #header>
<div class="title">
插件编辑
<span class="sub">
<span class="name">{{ plugin.title }} {{ plugin.author }}/{{ plugin.name }} </span>
</span>
</div>
</template>
<div class="pi-plugin-editor">
<div class="metadata">
<a-tabs v-model:active-key="metadataActive" type="card">
<a-tab-pane key="editor" tab="元数据"> </a-tab-pane>
<a-tab-pane key="source" tab="yaml"> </a-tab-pane>
</a-tabs>
<div class="metadata-body">
<a-tabs v-if="metadataActive === 'editor'" class="metadata-editor" tab-position="left" type="card">
<a-tab-pane key="input" tab="输入">
<plugin-input></plugin-input>
</a-tab-pane>
<a-tab-pane key="output" tab="输出"></a-tab-pane>
<a-tab-pane key="dependLibs" tab="第三方依赖"></a-tab-pane>
<a-tab-pane key="dependPlugins" tab="插件依赖"></a-tab-pane>
</a-tabs>
<div v-if="metadataActive === 'source'" class="metadata-source">
<fs-editor-code v-model="plugin.metadata" language="yaml"></fs-editor-code>
</div>
</div>
</div>
<div class="script">
<a-tabs type="card">
<a-tab-pane key="script" tab="脚本"> </a-tab-pane>
</a-tabs>
<fs-editor-code v-model="plugin.content" language="javascript"></fs-editor-code>
</div>
</div>
</fs-page>
</template>
<script lang="ts" setup>
import { onMounted, ref, watch } from "vue";
import { useRoute } from "vue-router";
import * as api from "./api";
import PluginInput from "/@/views/sys/plugin/components/plugin-input.vue";
defineOptions({
name: "SysPluginEdit",
});
const route = useRoute();
const plugin = ref<any>({});
const metadataActive = ref("editor");
async function getPlugin() {
const id = route.query.id;
plugin.value = await api.GetObj(id);
}
onMounted(async () => {
await getPlugin();
});
</script>
<style lang="less">
.page-plugin-edit {
.pi-plugin-editor {
display: flex;
flex-direction: row;
width: 100%;
height: 100%;
padding: 20px;
.fs-editor-code {
height: 100%;
flex: 1;
overflow-y: hidden;
}
.metadata {
width: 500px;
margin-right: 20px;
display: flex;
flex-direction: column;
height: 100%;
.metadata-body {
height: 100%;
flex: 1;
overflow-y: hidden;
}
.metadata-editor {
height: 100%;
flex: 1;
overflow-y: hidden;
.ant-tabs-content {
height: 100%;
}
}
.metadata-source {
height: 100%;
}
}
.script {
flex: 1;
display: flex;
flex-direction: column;
height: 100%;
}
}
}
</style>
@@ -47,23 +47,23 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
props: {
multiple: true,
crossPage: true,
selectedRowKeys
}
}
}
selectedRowKeys,
},
},
},
},
request: {
pageRequest,
addRequest,
editRequest,
delRequest
delRequest,
},
actionbar: {
buttons: {
add: {
show: false
}
}
show: false,
},
},
},
toolbar: { show: false },
rowHandle: {
@@ -71,13 +71,13 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
fixed: "right",
buttons: {
view: {
show: false
show: false,
},
copy: {
show: false
show: false,
},
edit: {
show: false
show: false,
},
syncStatus: {
show: compute(({ row }) => {
@@ -92,9 +92,9 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
onOk: async () => {
await api.SyncStatus(row.id);
await crudExpose.doRefresh();
}
},
});
}
},
},
updatePaid: {
show: compute(({ row }) => {
@@ -109,15 +109,15 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
onOk: async () => {
await api.UpdatePaid(row.id);
await crudExpose.doRefresh();
}
},
});
}
}
}
},
},
},
},
tabs: {
name: "status",
show: true
show: true,
},
columns: {
id: {
@@ -125,30 +125,30 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
key: "id",
type: "number",
column: {
width: 100
width: 100,
},
form: {
show: false
}
show: false,
},
},
tradeNo: {
title: "订单号",
type: "text",
search: { show: true },
form: {
show: false
show: false,
},
column: {
width: 250
}
width: 250,
},
},
title: {
title: "商品名称",
type: "text",
search: { show: true },
column: {
width: 150
}
width: 150,
},
},
duration: {
title: "时长",
@@ -157,10 +157,10 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
width: 100,
component: {
name: DurationValue,
vModel: "modelValue"
vModel: "modelValue",
},
align: "center"
}
align: "center",
},
},
amount: {
title: "金额",
@@ -170,9 +170,9 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
component: {
name: PriceInput,
vModel: "modelValue",
edit: false
}
}
edit: false,
},
},
},
status: {
title: "状态",
@@ -182,13 +182,13 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
data: [
{ label: "待支付", value: "wait_pay", color: "warning" },
{ label: "已支付", value: "paid", color: "success" },
{ label: "已关闭", value: "closed", color: "error" }
]
{ label: "已关闭", value: "closed", color: "error" },
],
}),
column: {
width: 100,
align: "center"
}
align: "center",
},
},
payType: {
title: "支付方式",
@@ -199,48 +199,48 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
{ label: "聚合支付", value: "yizhifu" },
{ label: "支付宝", value: "alipay" },
{ label: "微信", value: "wxpay" },
{ label: "免费", value: "free" }
]
{ label: "免费", value: "free" },
],
}),
column: {
width: 100,
component: {
color: "auto"
color: "auto",
},
align: "center"
}
align: "center",
},
},
payTime: {
title: "支付时间",
type: "datetime",
column: {
width: 160
}
width: 160,
},
},
createTime: {
title: "创建时间",
type: "datetime",
form: {
show: false
show: false,
},
column: {
sorter: true,
width: 160,
align: "center"
}
align: "center",
},
},
updateTime: {
title: "更新时间",
type: "datetime",
form: {
show: false
show: false,
},
column: {
show: true,
width: 160
}
}
}
}
width: 160,
},
},
},
},
};
}