mirror of
https://github.com/certd/certd.git
synced 2026-05-15 20:47:31 +08:00
build: trident-sync prepare
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
import * as api from "/@/views/certd/access/api";
|
||||
import { ref } from "vue";
|
||||
import { getCommonColumnDefine } from "/@/views/certd/access/common";
|
||||
|
||||
export default function ({ expose, props, ctx }) {
|
||||
const { crudBinding } = expose;
|
||||
const lastResRef = ref();
|
||||
const pageRequest = async (query) => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }) => {
|
||||
form.id = row.id;
|
||||
form.type = props.type;
|
||||
const res = await api.UpdateObj(form);
|
||||
lastResRef.value = res;
|
||||
return res;
|
||||
};
|
||||
const delRequest = async ({ row }) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
|
||||
const addRequest = async ({ form }) => {
|
||||
form.type = props.type;
|
||||
const res = await api.AddObj(form);
|
||||
lastResRef.value = res;
|
||||
return res;
|
||||
};
|
||||
|
||||
const selectedRowKey = ref([props.modelValue]);
|
||||
// watch(
|
||||
// () => {
|
||||
// return props.modelValue;
|
||||
// },
|
||||
// (value) => {
|
||||
// selectedRowKey.value = [value];
|
||||
// },
|
||||
// {
|
||||
// immediate: true
|
||||
// }
|
||||
// );
|
||||
const onSelectChange = (changed) => {
|
||||
selectedRowKey.value = changed;
|
||||
ctx.emit("update:modelValue", changed[0]);
|
||||
};
|
||||
|
||||
const typeRef = ref("aliyun");
|
||||
const commonColumnsDefine = getCommonColumnDefine(crudBinding, typeRef);
|
||||
commonColumnsDefine.type.form.component.disabled = true;
|
||||
return {
|
||||
typeRef,
|
||||
crudOptions: {
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
},
|
||||
toolbar: {
|
||||
show: false
|
||||
},
|
||||
search: {
|
||||
show: false
|
||||
},
|
||||
form: {
|
||||
wrapper: {
|
||||
width: "1050px"
|
||||
}
|
||||
},
|
||||
rowHandle: {
|
||||
width: "150px"
|
||||
},
|
||||
table: {
|
||||
rowSelection: {
|
||||
type: "radio",
|
||||
selectedRowKeys: selectedRowKey,
|
||||
onChange: onSelectChange
|
||||
},
|
||||
customRow: (record) => {
|
||||
return {
|
||||
onClick: () => {
|
||||
onSelectChange([record.id]);
|
||||
} // 点击行
|
||||
};
|
||||
}
|
||||
},
|
||||
columns: {
|
||||
id: {
|
||||
title: "ID",
|
||||
key: "id",
|
||||
type: "number",
|
||||
column: {
|
||||
width: 50
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
name: {
|
||||
title: "名称",
|
||||
search: {
|
||||
show: true
|
||||
},
|
||||
type: ["text"],
|
||||
form: {
|
||||
rules: [{ required: true, message: "请填写名称" }]
|
||||
}
|
||||
},
|
||||
...commonColumnsDefine
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<fs-page class="page-cert-access-modal">
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, onMounted, ref, watch } from "vue";
|
||||
import { useCrud, useExpose } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
|
||||
export default defineComponent({
|
||||
name: "CertAccessModal",
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
default: "aliyun"
|
||||
},
|
||||
modelValue: {}
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
setup(props, ctx) {
|
||||
// crud组件的ref
|
||||
const crudRef = ref();
|
||||
// crud 配置的ref
|
||||
const crudBinding = ref();
|
||||
// 暴露的方法
|
||||
const { expose } = useExpose({ crudRef, crudBinding });
|
||||
// 你的crud配置
|
||||
const { crudOptions, typeRef } = createCrudOptions({ expose, props, ctx });
|
||||
// 初始化crud配置
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-unused-vars
|
||||
const { resetCrudOptions } = useCrud({ expose, crudOptions });
|
||||
// 你可以调用此方法,重新初始化crud配置
|
||||
function onTypeChanged(value) {
|
||||
typeRef.value = value;
|
||||
expose.setSearchFormData({ form: { type: value }, mergeForm: true });
|
||||
expose.doRefresh();
|
||||
}
|
||||
watch(
|
||||
() => {
|
||||
return props.type;
|
||||
},
|
||||
(value) => {
|
||||
console.log("access type changed:", value);
|
||||
onTypeChanged(value);
|
||||
}
|
||||
);
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
onTypeChanged(props.type);
|
||||
});
|
||||
|
||||
return {
|
||||
crudBinding,
|
||||
crudRef
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="less">
|
||||
.page-cert-access {
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,103 @@
|
||||
<template>
|
||||
<div class="pi-access-selector">
|
||||
<span v-if="target.name" class="mlr-5">{{ target.name }}</span>
|
||||
<span v-else class="mlr-5 gray">请选择</span>
|
||||
<a-button @click="chooseForm.open">选择</a-button>
|
||||
<a-form-item-rest v-if="chooseForm.show">
|
||||
<a-modal v-model:visible="chooseForm.show" title="选择授权提供者" width="700px" @ok="chooseForm.ok">
|
||||
<div style="height: 400px; position: relative">
|
||||
<cert-access-modal v-model="selectedId" :type="type"></cert-access-modal>
|
||||
</div>
|
||||
</a-modal>
|
||||
</a-form-item-rest>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, reactive, ref, watch } from "vue";
|
||||
import * as api from "../api";
|
||||
import CertAccessModal from "./access/index.vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "PiAccessSelector",
|
||||
components: { CertAccessModal },
|
||||
props: {
|
||||
modelValue: {
|
||||
type: [Number, String],
|
||||
default: null
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: "aliyun"
|
||||
}
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
setup(props, ctx) {
|
||||
const target = ref({});
|
||||
const selectedId = ref();
|
||||
async function refreshTarget(value) {
|
||||
selectedId.value = value;
|
||||
if (value > 0) {
|
||||
target.value = await api.GetObj(value);
|
||||
}
|
||||
}
|
||||
watch(
|
||||
() => {
|
||||
return props.modelValue;
|
||||
},
|
||||
async (value) => {
|
||||
selectedId.value = null;
|
||||
target.value = {};
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
await refreshTarget(value);
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
);
|
||||
|
||||
const providerDefine = ref({});
|
||||
|
||||
async function refreshProviderDefine(type) {
|
||||
providerDefine.value = await api.GetProviderDefine(type);
|
||||
}
|
||||
watch(
|
||||
() => {
|
||||
return props.type;
|
||||
},
|
||||
async (value) => {
|
||||
await refreshProviderDefine(value);
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
);
|
||||
|
||||
const chooseForm = reactive({
|
||||
show: false,
|
||||
open() {
|
||||
chooseForm.show = true;
|
||||
},
|
||||
ok: () => {
|
||||
chooseForm.show = false;
|
||||
console.log("choose ok:", selectedId.value);
|
||||
refreshTarget(selectedId.value);
|
||||
ctx.emit("update:modelValue", selectedId.value);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
target,
|
||||
selectedId,
|
||||
providerDefine,
|
||||
chooseForm
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="less">
|
||||
.access-selector {
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,49 @@
|
||||
import { request } from "/src/api/service";
|
||||
const apiPrefix = "/pi/access";
|
||||
export function GetList(query) {
|
||||
return request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "post",
|
||||
data: query
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj) {
|
||||
return request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj) {
|
||||
return request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetObj(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/info",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetProviderDefine(type) {
|
||||
return request({
|
||||
url: apiPrefix + "/define",
|
||||
method: "post",
|
||||
params: { type }
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
import { dict } from "@fast-crud/fast-crud";
|
||||
import * as api from "./api";
|
||||
import _ from "lodash-es";
|
||||
|
||||
export function getCommonColumnDefine(crudBinding, typeRef) {
|
||||
const AccessTypeDictRef = dict({
|
||||
url: "/pi/access/accessTypeDict"
|
||||
});
|
||||
const defaultPluginConfig = {
|
||||
component: {
|
||||
name: "a-input",
|
||||
vModel: "value"
|
||||
}
|
||||
};
|
||||
|
||||
function buildDefineFields(define, mode) {
|
||||
const columns = crudBinding.value[mode + "Form"].columns;
|
||||
for (const key in columns) {
|
||||
if (key.indexOf(".") >= 0) {
|
||||
delete columns[key];
|
||||
}
|
||||
}
|
||||
console.log('crudBinding.value[mode + "Form"].columns', columns);
|
||||
_.forEach(define.input, (value, mapKey) => {
|
||||
const key = "access." + mapKey;
|
||||
const field = {
|
||||
...value,
|
||||
key
|
||||
};
|
||||
columns[key] = _.merge({ title: key }, defaultPluginConfig, field);
|
||||
console.log("form", crudBinding.value[mode + "Form"]);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
type: {
|
||||
title: "类型",
|
||||
type: "dict-select",
|
||||
dict: AccessTypeDictRef,
|
||||
search: {
|
||||
show: false
|
||||
},
|
||||
form: {
|
||||
component: {
|
||||
disabled: false
|
||||
},
|
||||
rules: [{ required: true, message: "请选择类型" }],
|
||||
valueChange: {
|
||||
immediate: true,
|
||||
async handle({ value, mode, form }) {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
const define = await api.GetProviderDefine(value);
|
||||
console.log("define", define);
|
||||
buildDefineFields(define, mode);
|
||||
}
|
||||
}
|
||||
},
|
||||
addForm: {
|
||||
value: typeRef
|
||||
}
|
||||
},
|
||||
setting: {
|
||||
column: { show: false },
|
||||
form: {
|
||||
show: false,
|
||||
valueBuilder({ value, form }) {
|
||||
form.access = {};
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
const setting = JSON.parse(value);
|
||||
for (const key in setting) {
|
||||
form.access[key] = setting[key];
|
||||
}
|
||||
},
|
||||
valueResolve({ form }) {
|
||||
const setting = form.access;
|
||||
form.setting = JSON.stringify(setting);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import * as api from "./api";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { ref } from "vue";
|
||||
import { getCommonColumnDefine } from "/@/views/certd/access/common";
|
||||
|
||||
export default function ({ expose }) {
|
||||
const { t } = useI18n();
|
||||
const pageRequest = async (query) => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }) => {
|
||||
form.id = row.id;
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({ row }) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
|
||||
const addRequest = async ({ form }) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
const typeRef = ref();
|
||||
const { crudBinding } = expose;
|
||||
const commonColumnsDefine = getCommonColumnDefine(crudBinding, typeRef);
|
||||
return {
|
||||
crudOptions: {
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
},
|
||||
form: {
|
||||
labelCol: {
|
||||
span: 6
|
||||
}
|
||||
},
|
||||
columns: {
|
||||
id: {
|
||||
title: "ID",
|
||||
key: "id",
|
||||
type: "number",
|
||||
column: {
|
||||
width: 50
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
name: {
|
||||
title: "名称",
|
||||
type: "text",
|
||||
form: {
|
||||
rules: [{ required: true, message: "必填项" }]
|
||||
}
|
||||
},
|
||||
...commonColumnsDefine
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<fs-page>
|
||||
<template #header>
|
||||
<div class="title">授权管理</div>
|
||||
</template>
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, onMounted } from "vue";
|
||||
import { useCrud } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
import { useExpose } from "@fast-crud/fast-crud";
|
||||
import { message } from "ant-design-vue";
|
||||
export default defineComponent({
|
||||
name: "CertdAccess",
|
||||
setup() {
|
||||
// crud组件的ref
|
||||
const crudRef = ref();
|
||||
// crud 配置的ref
|
||||
const crudBinding = ref();
|
||||
// 暴露的方法
|
||||
const { expose } = useExpose({ crudRef, crudBinding });
|
||||
// 你的crud配置
|
||||
const { crudOptions } = createCrudOptions({ expose });
|
||||
// 初始化crud配置
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-unused-vars
|
||||
const { resetCrudOptions } = useCrud({ expose, crudOptions });
|
||||
// 你可以调用此方法,重新初始化crud配置
|
||||
// resetCrudOptions(options)
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
expose.doRefresh();
|
||||
});
|
||||
|
||||
return {
|
||||
crudBinding,
|
||||
crudRef
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,35 @@
|
||||
import { request } from "/src/api/service";
|
||||
import { RunHistory } from "/@/views/certd/pipeline/pipeline/type";
|
||||
|
||||
const apiPrefix = "/pi/history";
|
||||
|
||||
export async function GetList(query) {
|
||||
const list = await request({
|
||||
url: apiPrefix + "/list",
|
||||
method: "post",
|
||||
data: query
|
||||
});
|
||||
for (const item of list) {
|
||||
if (item.pipeline) {
|
||||
item.pipeline = JSON.parse(item.pipeline);
|
||||
}
|
||||
}
|
||||
console.log("history", list);
|
||||
return list;
|
||||
}
|
||||
|
||||
export async function GetDetail(query): Promise<RunHistory> {
|
||||
const detail = await request({
|
||||
url: apiPrefix + "/detail",
|
||||
method: "post",
|
||||
params: query
|
||||
});
|
||||
|
||||
const pipeline = JSON.parse(detail.history?.pipeline || "{}");
|
||||
const logs = JSON.parse(detail.log?.logs || "{}");
|
||||
return {
|
||||
id: detail.history.id,
|
||||
pipeline,
|
||||
logs
|
||||
} as RunHistory;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { request } from "/src/api/service";
|
||||
import _ from "lodash-es";
|
||||
const apiPrefix = "/pi/plugin";
|
||||
|
||||
const defaultInputDefine = {
|
||||
component: {
|
||||
name: "a-input",
|
||||
vModel: "modelValue"
|
||||
}
|
||||
};
|
||||
|
||||
export async function GetList(query) {
|
||||
const plugins = await request({
|
||||
url: apiPrefix + "/list",
|
||||
method: "post",
|
||||
params: query
|
||||
});
|
||||
for (const plugin of plugins) {
|
||||
for (const key in plugin.input) {
|
||||
const field = _.merge({}, defaultInputDefine, plugin.input[key]);
|
||||
if (field.component.name === "a-input" || field.component.name === "a-select") {
|
||||
field.component.vModel = "value";
|
||||
}
|
||||
//嵌套对象
|
||||
field.key = ["input", key];
|
||||
if (field.required) {
|
||||
delete field.required;
|
||||
if (field.rules == null) {
|
||||
field.rules = [];
|
||||
}
|
||||
field.rules.push({ required: true, message: "此项必填" });
|
||||
}
|
||||
plugin.input[key] = field;
|
||||
}
|
||||
}
|
||||
console.log("plugins", plugins);
|
||||
return plugins;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
import { request } from "/src/api/service";
|
||||
const apiPrefix = "/pi/pipeline";
|
||||
|
||||
export function GetList(query) {
|
||||
return request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "post",
|
||||
data: query
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj) {
|
||||
return request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj) {
|
||||
return request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetObj(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/info",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetDetail(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/detail",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function Save(pipelineEntity) {
|
||||
return request({
|
||||
url: apiPrefix + "/save",
|
||||
method: "post",
|
||||
data: pipelineEntity
|
||||
});
|
||||
}
|
||||
|
||||
export function Trigger(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/trigger",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
import { compute } from "@fast-crud/fast-crud";
|
||||
import { Dicts } from "./dicts";
|
||||
|
||||
export default function () {
|
||||
return {
|
||||
crudOptions: {
|
||||
form: {
|
||||
wrapper: {
|
||||
width: "1150px"
|
||||
}
|
||||
},
|
||||
columns: {
|
||||
domains: {
|
||||
title: "域名",
|
||||
type: ["dict-select"],
|
||||
search: {
|
||||
show: true,
|
||||
component: {
|
||||
name: "a-input"
|
||||
}
|
||||
},
|
||||
form: {
|
||||
col: {
|
||||
span: 24
|
||||
},
|
||||
wrapperCol: {
|
||||
span: null
|
||||
},
|
||||
component: {
|
||||
mode: "tags",
|
||||
open: false
|
||||
},
|
||||
helper: {
|
||||
render: () => {
|
||||
return (
|
||||
<div>
|
||||
<div>支持通配符域名,例如: *.foo.com 、 *.test.handsfree.work</div>
|
||||
<div>支持多个域名、多个子域名、多个通配符域名打到一个证书上(域名必须是在同一个DNS提供商解析)</div>
|
||||
<div>多级子域名要分成多个域名输入(*.foo.com的证书不能用于xxx.yyy.foo.com)</div>
|
||||
<div>输入一个回车之后,再输入下一个</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
valueResolve({ form }) {
|
||||
if (form.domains instanceof String) {
|
||||
form.domains = form.domains?.join(",");
|
||||
}
|
||||
},
|
||||
rules: [{ required: true, message: "请填写域名" }]
|
||||
}
|
||||
},
|
||||
email: {
|
||||
title: "邮箱",
|
||||
type: "text",
|
||||
search: { show: false },
|
||||
form: {
|
||||
rules: [{ required: true, type: "email", message: "请填写邮箱" }]
|
||||
}
|
||||
},
|
||||
dnsProviderType: {
|
||||
title: "DNS提供商",
|
||||
type: "dict-select",
|
||||
dict: Dicts.dnsProviderTypeDict,
|
||||
form: {
|
||||
value: "aliyun",
|
||||
rules: [{ required: true, message: "请选择DNS提供商" }],
|
||||
valueChange({ form }) {
|
||||
form.dnsProviderAccess = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
dnsProviderAccess: {
|
||||
title: "DNS授权",
|
||||
type: "text",
|
||||
form: {
|
||||
component: {
|
||||
name: "PiAccessSelector",
|
||||
type: compute(({ form }) => {
|
||||
return form.dnsProviderType;
|
||||
}),
|
||||
vModel: "modelValue"
|
||||
},
|
||||
rules: [{ required: true, message: "请选择DNS授权" }]
|
||||
}
|
||||
}
|
||||
// country: {
|
||||
// title: "国家",
|
||||
// type: "text",
|
||||
// form: {
|
||||
// value: "China"
|
||||
// }
|
||||
// },
|
||||
// state: {
|
||||
// title: "省份",
|
||||
// type: "text",
|
||||
// form: {
|
||||
// value: "GuangDong"
|
||||
// }
|
||||
// },
|
||||
// locality: {
|
||||
// title: "市区",
|
||||
// type: "text",
|
||||
// form: {
|
||||
// value: "NanShan"
|
||||
// }
|
||||
// },
|
||||
// organization: {
|
||||
// title: "单位",
|
||||
// type: "text",
|
||||
// form: {
|
||||
// value: "CertD"
|
||||
// }
|
||||
// },
|
||||
// organizationUnit: {
|
||||
// title: "部门",
|
||||
// type: "text",
|
||||
// form: {
|
||||
// value: "IT Dept"
|
||||
// }
|
||||
// },
|
||||
// remark: {
|
||||
// title: "备注",
|
||||
// type: "text"
|
||||
// }
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { dict } from "@fast-crud/fast-crud";
|
||||
|
||||
export const Dicts = {
|
||||
certIssuerDict: dict({ data: [{ value: "letencrypt", label: "LetEncrypt" }] }),
|
||||
challengeTypeDict: dict({ data: [{ value: "dns", label: "DNS校验" }] }),
|
||||
dnsProviderTypeDict: dict({
|
||||
url: "pi/dnsProvider/dnsProviderTypeDict"
|
||||
})
|
||||
};
|
||||
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<fs-form-wrapper ref="formWrapperRef" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { useColumns, useExpose } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud.jsx";
|
||||
import { ref } from "vue";
|
||||
import _ from "lodash-es";
|
||||
export default {
|
||||
name: "PiCertdForm",
|
||||
setup(props, ctx) {
|
||||
// 自定义表单配置
|
||||
const { buildFormOptions } = useColumns();
|
||||
//使用crudOptions结构来构建自定义表单配置
|
||||
let { crudOptions } = createCrudOptions();
|
||||
const doSubmitRef = ref();
|
||||
const formOptions = buildFormOptions(
|
||||
_.merge(crudOptions, {
|
||||
form: {
|
||||
doSubmit({ form }) {
|
||||
// 创建certd 的pipeline
|
||||
doSubmitRef.value({ form });
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
const formWrapperRef = ref();
|
||||
const formWrapperOptions = ref();
|
||||
formWrapperOptions.value = formOptions;
|
||||
function open(doSubmit) {
|
||||
doSubmitRef.value = doSubmit;
|
||||
formWrapperRef.value.open(formWrapperOptions.value);
|
||||
}
|
||||
|
||||
return {
|
||||
formWrapperRef,
|
||||
open,
|
||||
formWrapperOptions
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,225 @@
|
||||
import * as api from "./api";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { ref, shallowRef } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { dict } from "@fast-crud/fast-crud";
|
||||
import { statusUtil } from "/@/views/certd/pipeline/pipeline/utils/util.status";
|
||||
import { nanoid } from "nanoid";
|
||||
import { message } from "ant-design-vue";
|
||||
export default function ({ expose, certdFormRef }) {
|
||||
const router = useRouter();
|
||||
const { t } = useI18n();
|
||||
const lastResRef = ref();
|
||||
const pageRequest = async (query) => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }) => {
|
||||
form.id = row.id;
|
||||
const res = await api.UpdateObj(form);
|
||||
lastResRef.value = res;
|
||||
return res;
|
||||
};
|
||||
const delRequest = async ({ row }) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
|
||||
const addRequest = async ({ form }) => {
|
||||
form.content = JSON.stringify({
|
||||
title: form.title
|
||||
});
|
||||
const res = await api.AddObj(form);
|
||||
lastResRef.value = res;
|
||||
return res;
|
||||
};
|
||||
function addCertdPipeline() {
|
||||
certdFormRef.value.open(async ({ form }) => {
|
||||
// 添加certd pipeline
|
||||
const pipeline = {
|
||||
title: form.domains[0] + "证书自动化",
|
||||
stages: [
|
||||
{
|
||||
id: nanoid(),
|
||||
title: "证书申请阶段",
|
||||
tasks: [
|
||||
{
|
||||
id: nanoid(),
|
||||
title: "证书申请任务",
|
||||
steps: [
|
||||
{
|
||||
id: nanoid(),
|
||||
title: "申请证书",
|
||||
input: {
|
||||
renewDays: 20,
|
||||
...form
|
||||
},
|
||||
strategy: {
|
||||
runStrategy: 0 // 正常执行
|
||||
},
|
||||
type: "CertApply"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const id = await api.Save({
|
||||
content: JSON.stringify(pipeline),
|
||||
keepHistoryCount: 30
|
||||
});
|
||||
message.success("创建成功,请添加证书部署任务");
|
||||
router.push({ path: "/certd/pipeline/detail", query: { id, editMode: "true" } });
|
||||
});
|
||||
}
|
||||
return {
|
||||
crudOptions: {
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
},
|
||||
actionbar: {
|
||||
buttons: {
|
||||
add: {
|
||||
order: 5,
|
||||
text: "自定义流水线"
|
||||
},
|
||||
addCertd: {
|
||||
order: 1,
|
||||
text: "添加证书流水线",
|
||||
type: "primary",
|
||||
click() {
|
||||
addCertdPipeline();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
form: {
|
||||
afterSubmit({ form, res, mode }) {
|
||||
if (mode === "add") {
|
||||
router.push({ path: "/certd/pipeline/detail", query: { id: res.id, editMode: "true" } });
|
||||
}
|
||||
}
|
||||
},
|
||||
rowHandle: {
|
||||
buttons: {
|
||||
view: {
|
||||
click({ row }) {
|
||||
router.push({ path: "/certd/pipeline/detail", query: { id: row.id, editMode: "false" } });
|
||||
}
|
||||
},
|
||||
config: {
|
||||
order: 1,
|
||||
title: null,
|
||||
type: "link",
|
||||
icon: "ant-design:edit-outlined",
|
||||
click({ row }) {
|
||||
router.push({ path: "/certd/pipeline/detail", query: { id: row.id, editMode: "true" } });
|
||||
}
|
||||
},
|
||||
edit: {
|
||||
order: 2,
|
||||
icon: "ant-design:setting-outlined"
|
||||
},
|
||||
remove: {
|
||||
order: 5
|
||||
}
|
||||
}
|
||||
},
|
||||
columns: {
|
||||
id: {
|
||||
title: "ID",
|
||||
key: "id",
|
||||
type: "number",
|
||||
column: {
|
||||
width: 50
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
title: {
|
||||
title: "流水线名称",
|
||||
type: "text",
|
||||
search: {
|
||||
show: true,
|
||||
component: {
|
||||
name: "a-input"
|
||||
}
|
||||
},
|
||||
column: {
|
||||
width: 300
|
||||
}
|
||||
},
|
||||
lastHistoryTime: {
|
||||
title: "最后运行",
|
||||
type: "datetime",
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
status: {
|
||||
title: "状态",
|
||||
type: "dict-select",
|
||||
dict: dict({
|
||||
data: statusUtil.getOptions()
|
||||
}),
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
|
||||
disabled: {
|
||||
title: "启用",
|
||||
type: "dict-switch",
|
||||
dict: dict({
|
||||
data: [
|
||||
{ value: true, label: "禁用" },
|
||||
{ value: false, label: "启用" }
|
||||
]
|
||||
}),
|
||||
form: {
|
||||
value: false,
|
||||
show: false
|
||||
},
|
||||
column: {
|
||||
component: {
|
||||
name: "fs-dict-switch",
|
||||
vModel: "checked"
|
||||
},
|
||||
async valueChange({ row, key, value }) {
|
||||
return await api.UpdateObj({
|
||||
id: row.id,
|
||||
disabled: row[key]
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
keepHistoryCount: {
|
||||
title: "历史记录保持数",
|
||||
type: "number",
|
||||
form: {
|
||||
value: 30,
|
||||
helper: "历史记录保持条数,多余的会被删除"
|
||||
}
|
||||
},
|
||||
createTime: {
|
||||
title: "创建时间",
|
||||
type: "datetime",
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
updateTime: {
|
||||
title: "更新时间",
|
||||
type: "datetime",
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
https://stackoverflow.com/questions/28365839/dashed-border-animation-in-css3-animation
|
||||
@@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<fs-page class="fs-pipeline-detail">
|
||||
<pipeline-edit v-model:edit-mode="editMode" :pipeline-id="pipelineId" :options="pipelineOptions"></pipeline-edit>
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, Ref, ref } from "vue";
|
||||
import PipelineEdit from "./pipeline/index.vue";
|
||||
import * as pluginApi from "./api.plugin";
|
||||
import * as historyApi from "./api.history";
|
||||
import * as api from "./api";
|
||||
import { useRoute } from "vue-router";
|
||||
import { Pipeline, PipelineDetail, PipelineOptions, RunHistory } from "/@/views/certd/pipeline/pipeline/type";
|
||||
import { PluginDefine } from "@certd/pipeline/src";
|
||||
|
||||
export default defineComponent({
|
||||
name: "PipelineDetail",
|
||||
components: { PipelineEdit },
|
||||
setup() {
|
||||
const route = useRoute();
|
||||
const pipelineId = ref(route.query.id);
|
||||
|
||||
const getPipelineDetail = async ({ pipelineId }) => {
|
||||
const detail = await api.GetDetail(pipelineId);
|
||||
return {
|
||||
pipeline: {
|
||||
id: detail.pipeline.id,
|
||||
stages: [],
|
||||
triggers: [],
|
||||
...JSON.parse(detail.pipeline.content || "{}")
|
||||
}
|
||||
} as PipelineDetail;
|
||||
};
|
||||
|
||||
const getHistoryList = async ({ pipelineId }) => {
|
||||
const list: RunHistory[] = await historyApi.GetList({ pipelineId });
|
||||
return list;
|
||||
};
|
||||
|
||||
const getHistoryDetail = async ({ historyId }): Promise<RunHistory> => {
|
||||
const detail = await historyApi.GetDetail({ id: historyId });
|
||||
return detail;
|
||||
};
|
||||
|
||||
const getPlugins = async () => {
|
||||
const plugins = await pluginApi.GetList({});
|
||||
return plugins as PluginDefine[];
|
||||
};
|
||||
|
||||
async function doSave(pipelineConfig: Pipeline) {
|
||||
await api.Save({
|
||||
id: pipelineConfig.id,
|
||||
content: JSON.stringify(pipelineConfig)
|
||||
});
|
||||
}
|
||||
async function doTrigger({ pipelineId }) {
|
||||
await api.Trigger(pipelineId);
|
||||
}
|
||||
|
||||
const pipelineOptions: Ref<PipelineOptions> = ref({
|
||||
doTrigger,
|
||||
doSave,
|
||||
getPlugins,
|
||||
getHistoryList,
|
||||
getHistoryDetail,
|
||||
getPipelineDetail
|
||||
});
|
||||
|
||||
const editMode = ref(false);
|
||||
if (route.query.editMode !== "false") {
|
||||
editMode.value = true;
|
||||
}
|
||||
|
||||
return {
|
||||
pipelineOptions,
|
||||
pipelineId,
|
||||
editMode
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="less">
|
||||
.page-pipeline-detail {
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<fs-page class="page-cert">
|
||||
<template #header>
|
||||
<div class="title">我的流水线</div>
|
||||
</template>
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding">
|
||||
<pi-certd-form ref="certdFormRef"></pi-certd-form>
|
||||
</fs-crud>
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted } from "vue";
|
||||
import { useCrud } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
import { useExpose } from "@fast-crud/fast-crud";
|
||||
import PiCertdForm from "./certd-form/index.vue";
|
||||
export default defineComponent({
|
||||
name: "PipelineManager",
|
||||
components: { PiCertdForm },
|
||||
setup() {
|
||||
const certdFormRef = ref();
|
||||
|
||||
// crud组件的ref
|
||||
const crudRef = ref();
|
||||
// crud 配置的ref
|
||||
const crudBinding = ref();
|
||||
|
||||
// 暴露的方法
|
||||
const { expose } = useExpose({ crudRef, crudBinding });
|
||||
// 你的crud配置
|
||||
const { crudOptions } = createCrudOptions({ expose, certdFormRef });
|
||||
// 初始化crud配置
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-unused-vars
|
||||
const { resetCrudOptions } = useCrud({ expose, crudOptions });
|
||||
// 你可以调用此方法,重新初始化crud配置
|
||||
// resetCrudOptions(options)
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
expose.doRefresh();
|
||||
});
|
||||
|
||||
return {
|
||||
crudBinding,
|
||||
crudRef,
|
||||
certdFormRef
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="less"></style>
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<a-timeline-item v-if="status && runnable" class="pi-history-timeline-item" :color="status.color">
|
||||
<template #dot>
|
||||
<fs-icon v-bind="status" />
|
||||
</template>
|
||||
<p>
|
||||
<fs-date-format :model-value="runnable.status?.startTime"></fs-date-format>
|
||||
<a-tag class="ml-10" :color="status.color">{{ status.label }}</a-tag>
|
||||
|
||||
<a-tag v-if="isCurrent" class="pointer" color="green" :closable="true" @close="cancel">当前</a-tag>
|
||||
<a-tag v-else-if="!editMode" class="pointer" color="blue" @click="view">查看</a-tag>
|
||||
</p>
|
||||
</a-timeline-item>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, provide, Ref, watch, computed } from "vue";
|
||||
import { statusUtil } from "/@/views/certd/pipeline/pipeline/utils/util.status";
|
||||
export default defineComponent({
|
||||
name: "PiHistoryTimelineItem",
|
||||
props: {
|
||||
runnable: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: "icon"
|
||||
},
|
||||
isCurrent: {
|
||||
type: Boolean
|
||||
},
|
||||
editMode: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
emits: ["view", "cancel"],
|
||||
setup(props, ctx) {
|
||||
const status = computed(() => {
|
||||
return statusUtil.get(props.runnable?.status?.result);
|
||||
});
|
||||
|
||||
function view() {
|
||||
ctx.emit("view");
|
||||
}
|
||||
function cancel() {
|
||||
ctx.emit("cancel");
|
||||
}
|
||||
return {
|
||||
status,
|
||||
cancel,
|
||||
view
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="less">
|
||||
.pi-history-timeline-item {
|
||||
.ant-tag.pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<a-select class="pi-output-selector" :value="modelValue" :options="options" @update:value="onChanged"> </a-select>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { inject, onMounted, Ref, ref, watch } from "vue";
|
||||
import { pluginManager } from "../../plugin";
|
||||
|
||||
export default {
|
||||
name: "PiOutputSelector",
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
setup(props, ctx) {
|
||||
const options = ref<any[]>([]);
|
||||
|
||||
const pipeline = inject("pipeline") as Ref<any>;
|
||||
const currentStageIndex = inject("currentStageIndex") as Ref<number>;
|
||||
const currentStepIndex = inject("currentStepIndex") as Ref<number>;
|
||||
const currentTask = inject("currentTask") as Ref<any>;
|
||||
|
||||
function onCreate() {
|
||||
options.value = pluginManager.getPreStepOutputOptions({
|
||||
pipeline: pipeline.value,
|
||||
currentStageIndex: currentStageIndex.value,
|
||||
currentStepIndex: currentStepIndex.value,
|
||||
currentTask: currentTask.value
|
||||
});
|
||||
if (props.modelValue == null && options.value.length > 0) {
|
||||
ctx.emit("update:modelValue", options.value[0].value);
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
onCreate();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => {
|
||||
return pluginManager.map;
|
||||
},
|
||||
() => {
|
||||
onCreate();
|
||||
}
|
||||
);
|
||||
|
||||
function onChanged(value) {
|
||||
ctx.emit("update:modelValue", value);
|
||||
}
|
||||
return {
|
||||
options,
|
||||
onChanged
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less"></style>
|
||||
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<span v-if="statusRef" class="pi-status-show">
|
||||
<template v-if="type === 'icon'">
|
||||
<fs-icon class="status-icon" v-bind="statusRef" :style="{ color: statusRef.color }" />
|
||||
</template>
|
||||
<template v-if="type === 'tag'">
|
||||
<a-tag :color="statusRef.color">{{ statusRef.label }}</a-tag>
|
||||
</template>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, provide, Ref, watch, computed } from "vue";
|
||||
import { statusUtil } from "/@/views/certd/pipeline/pipeline/utils/util.status";
|
||||
export default defineComponent({
|
||||
name: "PiStatusShow",
|
||||
props: {
|
||||
status: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: "icon"
|
||||
}
|
||||
},
|
||||
setup(props, ctx) {
|
||||
const statusRef = computed(() => {
|
||||
return statusUtil.get(props.status);
|
||||
});
|
||||
|
||||
return {
|
||||
statusRef
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="less">
|
||||
.pi-status-show {
|
||||
.status-icon {
|
||||
font-size: 16px;
|
||||
margin-left: 3px;
|
||||
margin-right: 3px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
+314
@@ -0,0 +1,314 @@
|
||||
<template>
|
||||
<a-drawer v-model:visible="stepDrawerVisible" placement="right" :closable="true" width="600px" :after-visible-change="stepDrawerOnAfterVisibleChange">
|
||||
<template #title>
|
||||
编辑任务
|
||||
<a-button v-if="editMode" @click="stepDelete()">
|
||||
<template #icon><DeleteOutlined /></template>
|
||||
</a-button>
|
||||
</template>
|
||||
<template v-if="currentStep">
|
||||
<pi-container v-if="currentStep._isAdd" class="pi-step-form">
|
||||
<a-row :gutter="10">
|
||||
<a-col v-for="(item, index) of stepPluginDefineList" :key="index" class="step-plugin" :span="12">
|
||||
<a-card
|
||||
hoverable
|
||||
:class="{ current: item.name === currentStep.type }"
|
||||
@click="stepTypeSelected(item)"
|
||||
@dblclick="
|
||||
stepTypeSelected(item);
|
||||
stepTypeSave();
|
||||
"
|
||||
>
|
||||
<a-card-meta>
|
||||
<template #title>
|
||||
<a-avatar :src="item.icon || '/images/plugin.png'" />
|
||||
<span class="title">{{ item.title }}</span>
|
||||
</template>
|
||||
<template #description>
|
||||
<span :title="item.desc">{{ item.desc }}</span>
|
||||
</template>
|
||||
</a-card-meta>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-button v-if="editMode" type="primary" @click="stepTypeSave"> 确定 </a-button>
|
||||
</pi-container>
|
||||
<pi-container v-else class="pi-step-form">
|
||||
<a-form ref="stepFormRef" class="step-form" :model="currentStep" :label-col="labelCol" :wrapper-col="wrapperCol">
|
||||
<div class="mb-10">
|
||||
<a-alert type="info" :message="currentPlugin.title" :description="currentPlugin.desc"> </a-alert>
|
||||
</div>
|
||||
<fs-form-item
|
||||
v-model="currentStep.title"
|
||||
:item="{
|
||||
title: '任务名称',
|
||||
key: 'title',
|
||||
component: {
|
||||
name: 'a-input',
|
||||
vModel: 'value'
|
||||
},
|
||||
rules: [{ required: true, message: '此项必填' }]
|
||||
}"
|
||||
:get-context-fn="blankFn"
|
||||
/>
|
||||
<template v-for="(item, key) in currentPlugin.input" :key="key">
|
||||
<fs-form-item v-model="currentStep.input[key]" :item="item" :get-context-fn="blankFn" />
|
||||
</template>
|
||||
|
||||
<fs-form-item
|
||||
v-model="currentStep.strategy.runStrategy"
|
||||
:item="{
|
||||
title: '运行策略',
|
||||
key: 'strategy.runStrategy',
|
||||
component: {
|
||||
name: 'a-select',
|
||||
vModel: 'value',
|
||||
options: [
|
||||
{ value: 0, label: '正常运行' },
|
||||
{ value: 1, label: '成功后跳过' }
|
||||
]
|
||||
},
|
||||
rules: [{ required: true, message: '此项必填' }]
|
||||
}"
|
||||
:get-context-fn="blankFn"
|
||||
/>
|
||||
</a-form>
|
||||
|
||||
<template #footer>
|
||||
<a-form-item v-if="editMode" :wrapper-col="{ span: 14, offset: 4 }">
|
||||
<a-button type="primary" @click="stepSave"> 确定 </a-button>
|
||||
</a-form-item>
|
||||
</template>
|
||||
</pi-container>
|
||||
</template>
|
||||
</a-drawer>
|
||||
</template>
|
||||
|
||||
<script lang="jsx">
|
||||
import { message, Modal } from "ant-design-vue";
|
||||
import { inject, ref } from "vue";
|
||||
import _ from "lodash-es";
|
||||
import { nanoid } from "nanoid";
|
||||
export default {
|
||||
name: "PiStepForm",
|
||||
props: {
|
||||
editMode: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
emits: ["update"],
|
||||
setup(props, context) {
|
||||
/**
|
||||
* step drawer
|
||||
* @returns
|
||||
*/
|
||||
function useStepForm() {
|
||||
const stepPluginDefineList = inject("plugins");
|
||||
|
||||
const mode = ref("add");
|
||||
const callback = ref();
|
||||
const currentStep = ref({ title: undefined, input: {} });
|
||||
const currentPlugin = ref({});
|
||||
const stepFormRef = ref(null);
|
||||
const stepDrawerVisible = ref(false);
|
||||
const rules = ref({
|
||||
name: [
|
||||
{
|
||||
type: "string",
|
||||
required: true,
|
||||
message: "请输入名称"
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const stepTypeSelected = (item) => {
|
||||
currentStep.value.type = item.name;
|
||||
currentStep.value.title = item.title;
|
||||
console.log("currentStepTypeChanged:", currentStep.value);
|
||||
};
|
||||
|
||||
const stepTypeSave = () => {
|
||||
currentStep.value._isAdd = false;
|
||||
if (currentStep.value.type == null) {
|
||||
message.warn("请先选择类型");
|
||||
return;
|
||||
}
|
||||
// 给step的input设置默认值
|
||||
changeCurrentPlugin(currentStep.value);
|
||||
|
||||
//赋初始值
|
||||
_.merge(currentStep.value, { input: {}, strategy: { runStrategy: 0 } }, currentPlugin.value.default, currentStep.value);
|
||||
|
||||
for (const key in currentPlugin.value.input) {
|
||||
const input = currentPlugin.value.input[key];
|
||||
if (input.default != null) {
|
||||
currentStep.value.input[key] = input.default ?? input.value;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const stepDrawerShow = () => {
|
||||
stepDrawerVisible.value = true;
|
||||
};
|
||||
const stepDrawerClose = () => {
|
||||
stepDrawerVisible.value = false;
|
||||
};
|
||||
|
||||
const stepDrawerOnAfterVisibleChange = (val) => {
|
||||
console.log("stepDrawerOnAfterVisibleChange", val);
|
||||
};
|
||||
|
||||
const stepOpen = (step, emit) => {
|
||||
callback.value = emit;
|
||||
currentStep.value = _.merge({ input: {}, strategy: {} }, step);
|
||||
console.log("currentStepOpen", currentStep.value);
|
||||
if (step.type) {
|
||||
changeCurrentPlugin(currentStep.value);
|
||||
}
|
||||
stepDrawerShow();
|
||||
};
|
||||
|
||||
const stepAdd = (emit) => {
|
||||
mode.value = "add";
|
||||
const step = {
|
||||
id: nanoid(),
|
||||
title: "新任务",
|
||||
type: undefined,
|
||||
_isAdd: true,
|
||||
input: {},
|
||||
status: null
|
||||
};
|
||||
stepOpen(step, emit);
|
||||
};
|
||||
|
||||
const stepEdit = (step, emit) => {
|
||||
mode.value = "edit";
|
||||
stepOpen(step, emit);
|
||||
};
|
||||
|
||||
const stepView = (step, emit) => {
|
||||
mode.value = "view";
|
||||
stepOpen(step, emit);
|
||||
};
|
||||
|
||||
const changeCurrentPlugin = (step) => {
|
||||
const stepType = step.type;
|
||||
const pluginDefine = stepPluginDefineList.value.find((p) => {
|
||||
return p.name === stepType;
|
||||
});
|
||||
if (pluginDefine) {
|
||||
step.type = stepType;
|
||||
step._isAdd = false;
|
||||
currentPlugin.value = pluginDefine;
|
||||
}
|
||||
console.log("currentStepTypeChanged:", currentStep.value);
|
||||
console.log("currentStepPlugin:", currentPlugin.value);
|
||||
};
|
||||
|
||||
const stepSave = async (e) => {
|
||||
console.log("currentStepSave", currentStep.value);
|
||||
try {
|
||||
await stepFormRef.value.validate();
|
||||
} catch (e) {
|
||||
console.error("表单验证失败:", e);
|
||||
return;
|
||||
}
|
||||
|
||||
callback.value("save", currentStep.value);
|
||||
stepDrawerClose();
|
||||
};
|
||||
|
||||
const stepDelete = () => {
|
||||
Modal.confirm({
|
||||
title: "确认",
|
||||
content: `确定要删除此步骤吗?`,
|
||||
async onOk() {
|
||||
callback.value("delete");
|
||||
stepDrawerClose();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const blankFn = () => {
|
||||
return {};
|
||||
};
|
||||
return {
|
||||
stepTypeSelected,
|
||||
stepTypeSave,
|
||||
stepPluginDefineList,
|
||||
stepFormRef,
|
||||
mode,
|
||||
stepAdd,
|
||||
stepEdit,
|
||||
stepView,
|
||||
stepDrawerShow,
|
||||
stepDrawerVisible,
|
||||
stepDrawerOnAfterVisibleChange,
|
||||
currentStep,
|
||||
currentPlugin,
|
||||
stepSave,
|
||||
stepDelete,
|
||||
rules,
|
||||
blankFn
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...useStepForm(),
|
||||
labelCol: { span: 6 },
|
||||
wrapperCol: { span: 16 }
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.pi-step-form {
|
||||
.body {
|
||||
padding: 10px;
|
||||
.ant-card {
|
||||
margin-bottom: 10px;
|
||||
|
||||
&.current {
|
||||
border-color: #00b7ff;
|
||||
}
|
||||
|
||||
.ant-card-meta-title {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.ant-avatar {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-left: 5px;
|
||||
white-space: nowrap;
|
||||
flex: 1;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-card-body {
|
||||
padding: 14px;
|
||||
height: 100px;
|
||||
|
||||
overflow-y: hidden;
|
||||
|
||||
.ant-card-meta-description {
|
||||
font-size: 10px;
|
||||
line-height: 20px;
|
||||
height: 40px;
|
||||
color: #7f7f7f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
+265
@@ -0,0 +1,265 @@
|
||||
<template>
|
||||
<a-drawer
|
||||
v-model:visible="taskDrawerVisible"
|
||||
placement="right"
|
||||
:closable="true"
|
||||
width="600px"
|
||||
class="pi-task-form"
|
||||
:after-visible-change="taskDrawerOnAfterVisibleChange"
|
||||
>
|
||||
<template #title>
|
||||
编辑任务
|
||||
<a-button v-if="editMode" @click="taskDelete()">
|
||||
<template #icon><DeleteOutlined /></template>
|
||||
</a-button>
|
||||
</template>
|
||||
<template v-if="currentTask">
|
||||
<pi-container>
|
||||
<a-form
|
||||
ref="taskFormRef"
|
||||
class="task-form"
|
||||
:model="currentTask"
|
||||
:label-col="labelCol"
|
||||
:wrapper-col="wrapperCol"
|
||||
>
|
||||
<fs-form-item
|
||||
v-model="currentTask.title"
|
||||
:item="{
|
||||
title: '任务名称',
|
||||
key: 'title',
|
||||
component: {
|
||||
name: 'a-input',
|
||||
vModel: 'value'
|
||||
},
|
||||
rules: [{ required: true, message: '此项必填' }]
|
||||
}"
|
||||
:get-context-fn="blankFn"
|
||||
/>
|
||||
|
||||
<div class="steps">
|
||||
<a-form-item
|
||||
:value="currentTask.steps"
|
||||
name="steps"
|
||||
label=""
|
||||
:wrapper-col="{ span: 24 }"
|
||||
:rules="[{ required: true, message: '至少需要一个步骤,或者你可以点击标题右边删除按钮删除此任务' }]"
|
||||
>
|
||||
<a-descriptions title="任务步骤" size="small">
|
||||
<template #extra>
|
||||
<a-button type="primary" @click="stepAdd(currentTask)">添加步骤</a-button>
|
||||
</template>
|
||||
</a-descriptions>
|
||||
<a-list class="step-list" item-layout="horizontal" :data-source="currentTask.steps">
|
||||
<template #renderItem="{ item, index }">
|
||||
<a-list-item>
|
||||
<template #actions>
|
||||
<a key="edit" @click="stepEdit(currentTask, item, index)">编辑</a>
|
||||
<a key="remove" @click="stepDelete(currentTask, index)">删除</a>
|
||||
</template>
|
||||
<a-list-item-meta>
|
||||
<template #title>
|
||||
{{ item.title }}
|
||||
</template>
|
||||
<template #avatar>
|
||||
<fs-icon icon="ion:flash"></fs-icon>
|
||||
</template>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
</a-form-item>
|
||||
</div>
|
||||
</a-form>
|
||||
|
||||
<pi-step-form ref="stepFormRef" :edit-mode="editMode"></pi-step-form>
|
||||
|
||||
<template #footer>
|
||||
<a-form-item v-if="editMode" :wrapper-col="{ span: 14, offset: 4 }">
|
||||
<a-button type="primary" @click="taskSave"> 确定 </a-button>
|
||||
</a-form-item>
|
||||
</template>
|
||||
</pi-container>
|
||||
</template>
|
||||
</a-drawer>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { provide, Ref, ref } from "vue";
|
||||
import _ from "lodash-es";
|
||||
import { nanoid } from "nanoid";
|
||||
import PiStepForm from "../step-form/index.vue";
|
||||
import { message, Modal } from "ant-design-vue";
|
||||
|
||||
export default {
|
||||
name: "PiTaskForm",
|
||||
components: { PiStepForm },
|
||||
props: {
|
||||
editMode: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
emits: ["update"],
|
||||
setup(props, ctx) {
|
||||
function useStep() {
|
||||
const stepFormRef: Ref<any> = ref(null);
|
||||
const currentStepIndex = ref(0);
|
||||
provide("currentStepIndex", currentStepIndex);
|
||||
const stepAdd = (task) => {
|
||||
currentStepIndex.value = task.steps.length;
|
||||
stepFormRef.value.stepAdd((type, value) => {
|
||||
if (type === "save") {
|
||||
task.steps.push(value);
|
||||
if (!task.title || task.title === "新任务") {
|
||||
task.title = value.title;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
const stepEdit = (task, step, stepIndex) => {
|
||||
currentStepIndex.value = stepIndex;
|
||||
console.log("step.edit start", task, step, props.editMode);
|
||||
if (props.editMode) {
|
||||
console.log("step.edit", task, step);
|
||||
stepFormRef.value.stepEdit(step, (type, value) => {
|
||||
console.log("step.save", step, type, value);
|
||||
if (type === "delete") {
|
||||
task.steps.splice(stepIndex, 1);
|
||||
} else if (type === "save") {
|
||||
task.steps[stepIndex] = { ...value };
|
||||
}
|
||||
console.log("task.steps", task.steps);
|
||||
});
|
||||
} else {
|
||||
stepFormRef.value.stepView(step, (type, value) => {});
|
||||
}
|
||||
};
|
||||
|
||||
const stepDelete = (task, stepIndex) => {
|
||||
Modal.confirm({
|
||||
title: "确认",
|
||||
content: `确定要删除此步骤吗?`,
|
||||
async onOk() {
|
||||
task.steps.splice(stepIndex, 1);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return { stepAdd, stepEdit, stepDelete, stepFormRef };
|
||||
}
|
||||
|
||||
/**
|
||||
* task drawer
|
||||
* @returns
|
||||
*/
|
||||
function useTaskForm() {
|
||||
const mode = ref("add");
|
||||
const callback = ref();
|
||||
const currentTask = ref({ title: undefined, steps: [] });
|
||||
provide("currentTask", currentTask);
|
||||
const taskFormRef: Ref<any> = ref(null);
|
||||
const taskDrawerVisible = ref(false);
|
||||
const rules = ref({
|
||||
name: [
|
||||
{
|
||||
type: "string",
|
||||
required: true,
|
||||
message: "请输入名称"
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const taskDrawerShow = () => {
|
||||
taskDrawerVisible.value = true;
|
||||
};
|
||||
const taskDrawerClose = () => {
|
||||
taskDrawerVisible.value = false;
|
||||
};
|
||||
|
||||
const taskDrawerOnAfterVisibleChange = (val) => {
|
||||
console.log("taskDrawerOnAfterVisibleChange", val);
|
||||
};
|
||||
|
||||
const taskOpen = (task, emit) => {
|
||||
callback.value = emit;
|
||||
currentTask.value = _.merge({ steps: {} }, task);
|
||||
console.log("currentTaskOpen", currentTask.value);
|
||||
taskDrawerShow();
|
||||
};
|
||||
|
||||
const taskAdd = (emit) => {
|
||||
mode.value = "add";
|
||||
const task = { id: nanoid(), title: "新任务", steps: [], status: null };
|
||||
taskOpen(task, emit);
|
||||
};
|
||||
|
||||
const taskEdit = (task, emit) => {
|
||||
mode.value = "edit";
|
||||
taskOpen(task, emit);
|
||||
};
|
||||
|
||||
const taskView = (task, emit) => {
|
||||
mode.value = "view";
|
||||
taskOpen(task, emit);
|
||||
};
|
||||
|
||||
const taskSave = async (e) => {
|
||||
console.log("currentTaskSave", currentTask.value);
|
||||
try {
|
||||
await taskFormRef.value.validate();
|
||||
} catch (e) {
|
||||
console.error("表单验证失败:", e);
|
||||
return;
|
||||
}
|
||||
|
||||
callback.value("save", currentTask.value);
|
||||
taskDrawerClose();
|
||||
};
|
||||
|
||||
const taskDelete = () => {
|
||||
Modal.confirm({
|
||||
title: "确认",
|
||||
content: `确定要删除此任务吗?`,
|
||||
async onOk() {
|
||||
callback.value("delete");
|
||||
taskDrawerClose();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const blankFn = () => {
|
||||
return {};
|
||||
};
|
||||
return {
|
||||
taskFormRef,
|
||||
mode,
|
||||
taskAdd,
|
||||
taskEdit,
|
||||
taskView,
|
||||
taskDrawerShow,
|
||||
taskDrawerVisible,
|
||||
taskDrawerOnAfterVisibleChange,
|
||||
currentTask,
|
||||
taskSave,
|
||||
taskDelete,
|
||||
rules,
|
||||
blankFn
|
||||
};
|
||||
}
|
||||
return {
|
||||
labelCol: { span: 6 },
|
||||
wrapperCol: { span: 16 },
|
||||
...useTaskForm(),
|
||||
...useStep()
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.pi-task-form {
|
||||
.steps {
|
||||
margin: 0 50px 0 50px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:visible="taskModal.visible"
|
||||
class="pi-task-view"
|
||||
title="任务日志"
|
||||
style="width: 80%"
|
||||
v-bind="taskModal"
|
||||
>
|
||||
<a-tabs v-model:activeKey="activeKey" tab-position="left" animated>
|
||||
<a-tab-pane v-for="item of detail.nodes" :key="item.node.id">
|
||||
<template #tab>
|
||||
<div class="tab-title" :title="item.node.title">
|
||||
<span class="tab-title-text">【{{ item.type }}】 {{ item.node.title }}</span>
|
||||
<pi-status-show :status="item.node.status?.result" type="icon"></pi-status-show>
|
||||
</div>
|
||||
</template>
|
||||
<pre
|
||||
class="pi-task-view-logs"
|
||||
><template v-for="(text, index) of item.logs" :key="index">{{ text }}</template></pre>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { inject, provide, Ref, ref } from "vue";
|
||||
import { RunHistory } from "/@/views/certd/pipeline/pipeline/type";
|
||||
import PiStatusShow from "/@/views/certd/pipeline/pipeline/component/status-show.vue";
|
||||
|
||||
export default {
|
||||
name: "PiTaskView",
|
||||
components: { PiStatusShow },
|
||||
props: {},
|
||||
setup(props, ctx) {
|
||||
const taskModal = ref({
|
||||
visible: false,
|
||||
onOk() {
|
||||
taskViewClose();
|
||||
},
|
||||
cancelText: "关闭"
|
||||
});
|
||||
|
||||
const detail = ref({ nodes: [] });
|
||||
const activeKey = ref();
|
||||
const currentHistory: Ref<RunHistory> | undefined = inject("currentHistory");
|
||||
const taskViewOpen = (task) => {
|
||||
taskModal.value.visible = true;
|
||||
const nodes: any = [];
|
||||
// nodes.push({
|
||||
// node: task,
|
||||
// type: "任务",
|
||||
// tab: 0,
|
||||
// logs: [],
|
||||
// result: {}
|
||||
// });
|
||||
for (let step of task.steps) {
|
||||
nodes.push({
|
||||
node: step,
|
||||
type: "步骤",
|
||||
tab: 2,
|
||||
logs: []
|
||||
});
|
||||
}
|
||||
for (let node of nodes) {
|
||||
if (currentHistory?.value?.logs != null) {
|
||||
node.logs = currentHistory.value.logs[node.node.id] || [];
|
||||
}
|
||||
}
|
||||
|
||||
if (task.steps.length > 0) {
|
||||
activeKey.value = task.steps[0].id;
|
||||
}
|
||||
|
||||
detail.value = { nodes };
|
||||
|
||||
console.log("nodes", nodes);
|
||||
};
|
||||
|
||||
const taskViewClose = () => {
|
||||
taskModal.value.visible = false;
|
||||
};
|
||||
|
||||
return {
|
||||
detail,
|
||||
taskModal,
|
||||
activeKey,
|
||||
taskViewOpen,
|
||||
taskViewClose
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.pi-task-view {
|
||||
.tab-title {
|
||||
display: flex;
|
||||
.tab-title-text {
|
||||
display: inline-block;
|
||||
width: 150px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
.pi-task-view-logs {
|
||||
background-color: #000c17;
|
||||
color: #fafafa;
|
||||
min-height: 300px;
|
||||
max-height: 580px;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
+208
@@ -0,0 +1,208 @@
|
||||
<template>
|
||||
<a-drawer
|
||||
v-model:visible="triggerDrawerVisible"
|
||||
placement="right"
|
||||
:closable="true"
|
||||
width="600px"
|
||||
class="pi-trigger-form"
|
||||
:after-visible-change="triggerDrawerOnAfterVisibleChange"
|
||||
>
|
||||
<template #title>
|
||||
编辑触发器
|
||||
<a-button v-if="mode === 'edit'" @click="triggerDelete()">
|
||||
<template #icon><DeleteOutlined /></template>
|
||||
</a-button>
|
||||
</template>
|
||||
<template v-if="currentTrigger">
|
||||
<pi-container>
|
||||
<a-form
|
||||
ref="triggerFormRef"
|
||||
class="trigger-form"
|
||||
:model="currentTrigger"
|
||||
:label-col="labelCol"
|
||||
:wrapper-col="wrapperCol"
|
||||
>
|
||||
<fs-form-item
|
||||
v-model="currentTrigger.title"
|
||||
:item="{
|
||||
title: '触发器名称',
|
||||
key: 'title',
|
||||
component: {
|
||||
name: 'a-input',
|
||||
vModel: 'value',
|
||||
disabled: !editMode
|
||||
},
|
||||
rules: [{ required: true, message: '此项必填' }]
|
||||
}"
|
||||
/>
|
||||
|
||||
<fs-form-item
|
||||
v-model="currentTrigger.type"
|
||||
:item="{
|
||||
title: '类型',
|
||||
key: 'type',
|
||||
value: 'timer',
|
||||
component: {
|
||||
name: 'a-select',
|
||||
vModel: 'value',
|
||||
disabled: !editMode,
|
||||
options: [{ value: 'timer', label: '定时' }]
|
||||
},
|
||||
rules: [{ required: true, message: '此项必填' }]
|
||||
}"
|
||||
/>
|
||||
|
||||
<fs-form-item
|
||||
v-model="currentTrigger.props.cron"
|
||||
:item="{
|
||||
title: '定时脚本',
|
||||
key: 'props.cron',
|
||||
component: {
|
||||
disabled: !editMode,
|
||||
name: 'a-input',
|
||||
vModel: 'value'
|
||||
},
|
||||
helper: 'cron表达式,例如: * * 3 * * * ,表示每天凌晨3点触发',
|
||||
rules: [{ required: true, message: '此项必填' }]
|
||||
}"
|
||||
/>
|
||||
</a-form>
|
||||
|
||||
<template #footer>
|
||||
<a-form-item v-if="editMode" :wrapper-col="{ span: 14, offset: 4 }">
|
||||
<a-button type="primary" @click="triggerSave"> 确定 </a-button>
|
||||
</a-form-item>
|
||||
</template>
|
||||
</pi-container>
|
||||
</template>
|
||||
</a-drawer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { message, Modal } from "ant-design-vue";
|
||||
import { inject, ref } from "vue";
|
||||
import _ from "lodash-es";
|
||||
import { nanoid } from "nanoid";
|
||||
export default {
|
||||
name: "PiTriggerForm",
|
||||
props: {
|
||||
editMode: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
emits: ["update"],
|
||||
setup(props, context) {
|
||||
/**
|
||||
* trigger drawer
|
||||
* @returns
|
||||
*/
|
||||
function useTriggerForm() {
|
||||
const mode = ref("add");
|
||||
const callback = ref();
|
||||
const currentTrigger = ref({ title: undefined, input: {} });
|
||||
const currentPlugin = ref({});
|
||||
const triggerFormRef = ref(null);
|
||||
const triggerDrawerVisible = ref(false);
|
||||
const rules = ref({
|
||||
name: [
|
||||
{
|
||||
type: "string",
|
||||
required: true,
|
||||
message: "请输入名称"
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const triggerDrawerShow = () => {
|
||||
triggerDrawerVisible.value = true;
|
||||
};
|
||||
const triggerDrawerClose = () => {
|
||||
triggerDrawerVisible.value = false;
|
||||
};
|
||||
|
||||
const triggerDrawerOnAfterVisibleChange = (val) => {
|
||||
console.log("triggerDrawerOnAfterVisibleChange", val);
|
||||
};
|
||||
|
||||
const triggerOpen = (trigger, emit) => {
|
||||
callback.value = emit;
|
||||
currentTrigger.value = _.cloneDeep(trigger);
|
||||
console.log("currentTriggerOpen", currentTrigger.value);
|
||||
triggerDrawerShow();
|
||||
};
|
||||
|
||||
const triggerAdd = (emit) => {
|
||||
mode.value = "add";
|
||||
const trigger = { id: nanoid(), title: "定时触发", type: "timer", props: {} };
|
||||
triggerOpen(trigger, emit);
|
||||
};
|
||||
|
||||
const triggerEdit = (trigger, emit) => {
|
||||
mode.value = "edit";
|
||||
triggerOpen(trigger, emit);
|
||||
};
|
||||
|
||||
const triggerView = (trigger, emit) => {
|
||||
mode.value = "view";
|
||||
triggerOpen(trigger, emit);
|
||||
};
|
||||
|
||||
const triggerSave = async (e) => {
|
||||
console.log("currentTriggerSave", currentTrigger.value);
|
||||
try {
|
||||
await triggerFormRef.value.validate();
|
||||
} catch (e) {
|
||||
console.error("表单验证失败:", e);
|
||||
return;
|
||||
}
|
||||
|
||||
callback.value("save", currentTrigger.value);
|
||||
triggerDrawerClose();
|
||||
};
|
||||
|
||||
const triggerDelete = () => {
|
||||
Modal.confirm({
|
||||
title: "确认",
|
||||
content: `确定要删除此触发器吗?`,
|
||||
async onOk() {
|
||||
callback.value("delete");
|
||||
triggerDrawerClose();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const blankFn = () => {
|
||||
return {};
|
||||
};
|
||||
return {
|
||||
triggerFormRef,
|
||||
mode,
|
||||
triggerAdd,
|
||||
triggerEdit,
|
||||
triggerView,
|
||||
triggerDrawerShow,
|
||||
triggerDrawerVisible,
|
||||
triggerDrawerOnAfterVisibleChange,
|
||||
currentTrigger,
|
||||
currentPlugin,
|
||||
triggerSave,
|
||||
triggerDelete,
|
||||
rules,
|
||||
blankFn
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...useTriggerForm(),
|
||||
labelCol: { span: 6 },
|
||||
wrapperCol: { span: 16 }
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.pi-trigger-form {
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,652 @@
|
||||
<template>
|
||||
<fs-page v-if="pipeline" class="page-pipeline-edit">
|
||||
<template #header>
|
||||
<div class="title">
|
||||
<pi-editable v-model="pipeline.title" :hover-show="false" :disabled="!editMode"></pi-editable>
|
||||
</div>
|
||||
<div class="more">
|
||||
<template v-if="editMode">
|
||||
<a-button type="primary" :loading="saveLoading" @click="save">保存</a-button>
|
||||
<a-button class="ml-5" @click="cancel">取消</a-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-button type="primary" @click="edit">编辑</a-button>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="layout">
|
||||
<div class="layout-left">
|
||||
<div class="pipeline-container">
|
||||
<div class="pipeline">
|
||||
<div class="stages">
|
||||
<div class="stage first-stage">
|
||||
<div class="title">
|
||||
<pi-editable model-value="触发源" :disabled="true" />
|
||||
</div>
|
||||
<div class="tasks">
|
||||
<div class="task-container first-task">
|
||||
<div class="line">
|
||||
<div class="flow-line"></div>
|
||||
</div>
|
||||
<div class="task">
|
||||
<a-button shape="round" type="primary" @click="run">
|
||||
<fs-icon icon="ion:play"></fs-icon>
|
||||
手动触发
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-for="(trigger, index) of pipeline.triggers" :key="trigger.id" class="task-container">
|
||||
<div class="line">
|
||||
<div class="flow-line"></div>
|
||||
</div>
|
||||
<div class="task">
|
||||
<a-button shape="round" @click="triggerEdit(trigger, index)">
|
||||
<fs-icon icon="ion:time"></fs-icon>
|
||||
{{ trigger.title }}
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="editMode" class="task-container is-add">
|
||||
<div class="line">
|
||||
<div class="flow-line"></div>
|
||||
</div>
|
||||
<div class="task">
|
||||
<a-button shape="round" type="dashed" @click="triggerAdd">
|
||||
<fs-icon icon="ion:add-circle-outline"></fs-icon>
|
||||
触发源(定时)
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-for="(stage, index) of pipeline.stages" :key="stage.id" class="stage" :class="{ 'last-stage': !editMode && index === pipeline.stages.length - 1 }">
|
||||
<div class="title">
|
||||
<pi-editable v-model="stage.title" :disabled="!editMode"></pi-editable>
|
||||
</div>
|
||||
<div class="tasks">
|
||||
<div
|
||||
v-for="(task, taskIndex) of stage.tasks"
|
||||
:key="task.id"
|
||||
class="task-container"
|
||||
:class="{
|
||||
'first-task': taskIndex === 0
|
||||
}"
|
||||
>
|
||||
<div class="line">
|
||||
<div class="flow-line"></div>
|
||||
<fs-icon v-if="editMode" class="add-stage-btn" title="添加新阶段" icon="ion:add-circle" @click="stageAdd(index)"></fs-icon>
|
||||
</div>
|
||||
<div class="task">
|
||||
<a-button shape="round" @click="taskEdit(stage, index, task, taskIndex)">
|
||||
{{ task.title }}
|
||||
<pi-status-show :status="task.status?.result"></pi-status-show>
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="editMode" class="task-container is-add">
|
||||
<div class="line">
|
||||
<div class="flow-line"></div>
|
||||
</div>
|
||||
<div class="task">
|
||||
<a-button type="dashed" shape="round" @click="taskAdd(stage, index)">
|
||||
<fs-icon class="font-20" icon="ion:add-circle-outline"></fs-icon>
|
||||
并行任务
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="editMode" class="stage last-stage">
|
||||
<div class="title">
|
||||
<pi-editable model-value="新阶段" :disabled="true" />
|
||||
</div>
|
||||
<div class="tasks">
|
||||
<div class="task-container first-task">
|
||||
<div class="line">
|
||||
<div class="flow-line"></div>
|
||||
<fs-icon class="add-stage-btn" title="添加新阶段" icon="ion:add-circle" @click="stageAdd()"></fs-icon>
|
||||
</div>
|
||||
<div class="task">
|
||||
<a-button shape="round" type="dashed" @click="stageAdd()">
|
||||
<fs-icon icon="ion:add-circle-outline"></fs-icon>
|
||||
新任务
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layout-right">
|
||||
<a-page-header title="运行历史" sub-title="点任务可查看日志" class="logs-block">
|
||||
<a-timeline class="mt-10">
|
||||
<template v-for="item of histories" :key="item.id">
|
||||
<pi-history-timeline-item :runnable="item.pipeline" :is-current="currentHistory?.id === item.id" :edit-mode="editMode" @view="historyView(item)" @cancel="historyCancel()"></pi-history-timeline-item>
|
||||
</template>
|
||||
<a-empty v-if="histories.length === 0"> </a-empty>
|
||||
</a-timeline>
|
||||
</a-page-header>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<pi-task-form ref="taskFormRef" :edit-mode="editMode"></pi-task-form>
|
||||
<pi-trigger-form ref="triggerFormRef" :edit-mode="editMode"></pi-trigger-form>
|
||||
<pi-task-view ref="taskViewRef"></pi-task-view>
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, provide, Ref, watch } from "vue";
|
||||
import PiTaskForm from "./component/task-form/index.vue";
|
||||
import PiTriggerForm from "./component/trigger-form/index.vue";
|
||||
import PiTaskView from "./component/task-view/index.vue";
|
||||
import PiStatusShow from "./component/status-show.vue";
|
||||
import _ from "lodash-es";
|
||||
import { message, Modal, notification } from "ant-design-vue";
|
||||
import { pluginManager } from "/@/views/certd/pipeline/pipeline/plugin";
|
||||
import { nanoid } from "nanoid";
|
||||
import { PipelineDetail, PipelineOptions, RunHistory, Runnable } from "/@/views/certd/pipeline/pipeline/type";
|
||||
import { statusUtil } from "./utils/util.status";
|
||||
import PiHistoryTimelineItem from "/@/views/certd/pipeline/pipeline/component/history-timeline-item.vue";
|
||||
export default defineComponent({
|
||||
name: "PipelineEdit",
|
||||
// eslint-disable-next-line vue/no-unused-components
|
||||
components: { PiHistoryTimelineItem, PiTaskForm, PiTriggerForm, PiTaskView, PiStatusShow },
|
||||
props: {
|
||||
pipelineId: {
|
||||
type: [Number, String],
|
||||
default: 0
|
||||
},
|
||||
editMode: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
options: {
|
||||
type: Object as PropType<PipelineOptions>,
|
||||
default() {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
},
|
||||
emits: ["update:modelValue", "update:editMode"],
|
||||
setup(props, ctx) {
|
||||
const currentPipeline: Ref<any> = ref({});
|
||||
const pipeline: Ref<any> = ref({});
|
||||
|
||||
const histories: Ref<RunHistory[]> = ref([]);
|
||||
|
||||
const currentHistory: Ref<any> = ref({});
|
||||
|
||||
const loadCurrentHistoryDetail = async () => {
|
||||
console.log("load history logs");
|
||||
const detail: RunHistory = await props.options?.getHistoryDetail({ historyId: currentHistory.value.id });
|
||||
currentHistory.value.logs = detail.logs;
|
||||
_.merge(currentHistory.value.pipeline, detail.pipeline);
|
||||
};
|
||||
const changeCurrentHistory = async (history?: RunHistory) => {
|
||||
if (!history) {
|
||||
//取消历史记录查看模式
|
||||
currentHistory.value = null;
|
||||
pipeline.value = currentPipeline.value;
|
||||
return;
|
||||
}
|
||||
currentHistory.value = history;
|
||||
pipeline.value = history.pipeline;
|
||||
await loadCurrentHistoryDetail();
|
||||
console.log("currentHistory:", currentHistory);
|
||||
};
|
||||
|
||||
async function loadHistoryList(reload = false) {
|
||||
if (props.editMode) {
|
||||
return;
|
||||
}
|
||||
if (reload) {
|
||||
histories.value = [];
|
||||
}
|
||||
console.log("load history list");
|
||||
const historyList = await props.options.getHistoryList({ pipelineId: pipeline.value.id });
|
||||
if (!historyList) {
|
||||
return;
|
||||
}
|
||||
if (histories.value.length > 0 && histories.value[0].id === historyList[0].id) {
|
||||
return;
|
||||
}
|
||||
histories.value = historyList;
|
||||
|
||||
if (historyList.length > 0) {
|
||||
if (historyList[0].pipeline?.version === pipeline.value.version) {
|
||||
await changeCurrentHistory(historyList[0]);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
const intervalLoadHistoryRef = ref();
|
||||
function watchNewHistoryList() {
|
||||
intervalLoadHistoryRef.value = setInterval(async () => {
|
||||
if (currentHistory.value == null) {
|
||||
await loadHistoryList();
|
||||
} else if (currentHistory.value.pipeline?.status?.status === "start") {
|
||||
await loadCurrentHistoryDetail();
|
||||
} else {
|
||||
clearInterval(intervalLoadHistoryRef.value);
|
||||
}
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
watch(
|
||||
() => {
|
||||
return props.editMode;
|
||||
},
|
||||
(editMode) => {
|
||||
if (editMode) {
|
||||
changeCurrentHistory();
|
||||
} else if (histories.value.length > 0) {
|
||||
if (histories.value[0].pipeline.version === pipeline.value.version) {
|
||||
changeCurrentHistory(histories.value[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
watch(
|
||||
() => {
|
||||
return props.pipelineId;
|
||||
},
|
||||
async (value) => {
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
const detail: PipelineDetail = await props.options.getPipelineDetail({ pipelineId: value });
|
||||
currentPipeline.value = _.merge({ title: "新管道流程", stages: [], triggers: [] }, detail.pipeline);
|
||||
pipeline.value = currentPipeline.value;
|
||||
await loadHistoryList(true);
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
);
|
||||
|
||||
const plugins: Ref<any> = ref([]);
|
||||
|
||||
const fetchPlugins = async () => {
|
||||
const list = await props.options.getPlugins();
|
||||
plugins.value = list;
|
||||
pluginManager.init(list);
|
||||
};
|
||||
fetchPlugins();
|
||||
|
||||
provide("pipeline", pipeline);
|
||||
provide("plugins", plugins);
|
||||
provide("currentHistory", currentHistory);
|
||||
|
||||
function useTask() {
|
||||
const taskFormRef: Ref<any> = ref(null);
|
||||
const currentStageIndex = ref(0);
|
||||
const currentTaskIndex = ref(0);
|
||||
provide("currentStageIndex", currentStageIndex);
|
||||
provide("currentTaskIndex", currentTaskIndex);
|
||||
|
||||
function useTaskView() {
|
||||
const taskViewRef: Ref<any> = ref(null);
|
||||
const taskViewOpen = (task) => {
|
||||
taskViewRef.value.open(task);
|
||||
};
|
||||
return {
|
||||
taskViewOpen,
|
||||
taskViewRef
|
||||
};
|
||||
}
|
||||
|
||||
const taskView = useTaskView();
|
||||
|
||||
const taskAdd = (stage: any, stageIndex: number, onSuccess?) => {
|
||||
currentStageIndex.value = stageIndex;
|
||||
currentTaskIndex.value = stage.tasks.length;
|
||||
taskFormRef.value.taskAdd((type, value) => {
|
||||
if (type === "save") {
|
||||
stage.tasks.push(value);
|
||||
if (onSuccess) {
|
||||
onSuccess();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
const taskEdit = (stage, stageIndex, task, taskIndex, onSuccess?) => {
|
||||
currentStageIndex.value = stageIndex;
|
||||
currentTaskIndex.value = taskIndex;
|
||||
if (taskFormRef.value == null) {
|
||||
return;
|
||||
}
|
||||
if (props.editMode) {
|
||||
taskFormRef.value.taskEdit(task, (type, value) => {
|
||||
if (type === "delete") {
|
||||
stage.tasks.splice(taskIndex, 1);
|
||||
if (stage.tasks.length === 0) {
|
||||
_.remove(pipeline.value.stages, (item: Runnable) => {
|
||||
return item.id === stage.id;
|
||||
});
|
||||
}
|
||||
} else if (type === "save") {
|
||||
stage.tasks[taskIndex] = value;
|
||||
}
|
||||
if (onSuccess) {
|
||||
onSuccess(type);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
taskView.taskViewRef.value.taskViewOpen(task);
|
||||
}
|
||||
};
|
||||
|
||||
return { taskAdd, taskEdit, taskFormRef, ...taskView };
|
||||
}
|
||||
|
||||
function useStage(useTaskRet) {
|
||||
const stageAdd = (stageIndex = pipeline.value.stages.length) => {
|
||||
const stage = {
|
||||
id: nanoid(),
|
||||
title: "新阶段",
|
||||
tasks: [],
|
||||
status: null
|
||||
};
|
||||
//stage: any, stageIndex: number, onSuccess
|
||||
useTaskRet.taskAdd(stage, stageIndex, () => {
|
||||
let task = stage.tasks[0] as any;
|
||||
stage.title = task.title + "阶段";
|
||||
//插入阶段
|
||||
pipeline.value.stages.splice(stageIndex, 0, stage);
|
||||
});
|
||||
};
|
||||
return {
|
||||
stageAdd
|
||||
};
|
||||
}
|
||||
|
||||
function useTrigger() {
|
||||
const triggerFormRef: Ref<any> = ref(null);
|
||||
const triggerAdd = () => {
|
||||
triggerFormRef.value.triggerAdd((type, value) => {
|
||||
if (type === "save") {
|
||||
pipeline.value.triggers.push(value);
|
||||
}
|
||||
});
|
||||
};
|
||||
const triggerEdit = (trigger, index) => {
|
||||
if (triggerFormRef.value == null) {
|
||||
return;
|
||||
}
|
||||
if (props.editMode) {
|
||||
triggerFormRef.value.triggerEdit(trigger, (type, value) => {
|
||||
if (type === "delete") {
|
||||
pipeline.value.triggers.splice(index, 1);
|
||||
} else if (type === "save") {
|
||||
pipeline.value.triggers[index] = value;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
triggerFormRef.value.triggerView(trigger, (type, value) => {});
|
||||
}
|
||||
};
|
||||
return {
|
||||
triggerAdd,
|
||||
triggerEdit,
|
||||
triggerFormRef
|
||||
};
|
||||
}
|
||||
|
||||
function useActions() {
|
||||
const saveLoading = ref();
|
||||
const run = async () => {
|
||||
if (props.editMode) {
|
||||
message.warn("请先保存,再运行管道");
|
||||
return;
|
||||
}
|
||||
if (!props.options.doTrigger) {
|
||||
message.warn("暂不支持运行");
|
||||
return;
|
||||
}
|
||||
if (pipeline.value.stages == null || pipeline.value.stages.length === 0) {
|
||||
message.warn("请先添加阶段和任务");
|
||||
return;
|
||||
}
|
||||
Modal.confirm({
|
||||
title: "确认",
|
||||
content: `确定要手动触发运行吗?`,
|
||||
async onOk() {
|
||||
//@ts-ignore
|
||||
await changeCurrentHistory(null);
|
||||
watchNewHistoryList();
|
||||
await props.options.doTrigger({ pipelineId: pipeline.value.id });
|
||||
notification.success({ message: "管道已经开始运行" });
|
||||
}
|
||||
});
|
||||
};
|
||||
function toggleEditMode(editMode: boolean) {
|
||||
ctx.emit("update:editMode", editMode);
|
||||
}
|
||||
const save = async () => {
|
||||
saveLoading.value = true;
|
||||
try {
|
||||
if (props.options.doSave) {
|
||||
pipeline.value.version++;
|
||||
currentPipeline.value = pipeline.value;
|
||||
await props.options.doSave(pipeline.value);
|
||||
}
|
||||
toggleEditMode(false);
|
||||
} finally {
|
||||
saveLoading.value = false;
|
||||
}
|
||||
};
|
||||
const edit = () => {
|
||||
pipeline.value = _.cloneDeep(currentPipeline.value);
|
||||
currentHistory.value = null;
|
||||
toggleEditMode(true);
|
||||
};
|
||||
const cancel = () => {
|
||||
pipeline.value = currentPipeline.value;
|
||||
toggleEditMode(false);
|
||||
};
|
||||
|
||||
return {
|
||||
run,
|
||||
save,
|
||||
edit,
|
||||
cancel,
|
||||
saveLoading
|
||||
};
|
||||
}
|
||||
|
||||
function useHistory() {
|
||||
const historyView = (history) => {
|
||||
changeCurrentHistory(history);
|
||||
console.log("currentPipeline", pipeline);
|
||||
};
|
||||
|
||||
const historyCancel = () => {
|
||||
changeCurrentHistory();
|
||||
console.log("currentPipeline", pipeline);
|
||||
};
|
||||
|
||||
return {
|
||||
historyView,
|
||||
historyCancel
|
||||
};
|
||||
}
|
||||
const useTaskRet = useTask();
|
||||
const useStageRet = useStage(useTaskRet);
|
||||
|
||||
return {
|
||||
pipeline,
|
||||
currentHistory,
|
||||
histories,
|
||||
...useTaskRet,
|
||||
...useStageRet,
|
||||
...useTrigger(),
|
||||
...useActions(),
|
||||
...useHistory()
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="less">
|
||||
.page-pipeline-edit {
|
||||
.fs-page-header {
|
||||
.title {
|
||||
.pi-editable {
|
||||
width: 300px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pi-status-show {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.layout {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
display: flex;
|
||||
.layout-left {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
}
|
||||
.layout-right {
|
||||
width: 350px;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
.pipeline-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
background-color: #f0f0f0;
|
||||
overflow: auto;
|
||||
}
|
||||
.pipeline {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
background-color: #f0f0f0;
|
||||
.stages {
|
||||
display: flex;
|
||||
overflow: auto;
|
||||
min-width: 100%;
|
||||
height: 100%;
|
||||
.stage {
|
||||
width: 300px;
|
||||
border-right: 1px solid #c7c7c7;
|
||||
.is-add {
|
||||
visibility: hidden;
|
||||
color: gray;
|
||||
}
|
||||
&:hover .is-add {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.title {
|
||||
padding: 20px;
|
||||
color: gray;
|
||||
}
|
||||
&.first-stage {
|
||||
.line {
|
||||
width: 50% !important;
|
||||
.flow-line {
|
||||
border-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.last-stage {
|
||||
.line {
|
||||
width: 50% !important;
|
||||
left: 0;
|
||||
right: auto;
|
||||
.flow-line {
|
||||
border-right: 0;
|
||||
}
|
||||
.add-stage-btn {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.line {
|
||||
height: 50px;
|
||||
position: absolute;
|
||||
top: -25px;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
.flow-line {
|
||||
height: 100%;
|
||||
margin-left: 28px;
|
||||
margin-right: 28px;
|
||||
border: 1px solid #c7c7c7;
|
||||
border-top: 0;
|
||||
}
|
||||
.add-stage-btn {
|
||||
display: inline-flex;
|
||||
visibility: hidden;
|
||||
font-size: 24px;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
bottom: -12px;
|
||||
left: -12px;
|
||||
z-index: 100;
|
||||
&:hover {
|
||||
color: #1890ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tasks {
|
||||
.task-container {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
&.first-task {
|
||||
.line {
|
||||
.flow-line {
|
||||
margin: 0;
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
}
|
||||
.add-stage-btn {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
.task {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
z-index: 2;
|
||||
|
||||
.ant-btn {
|
||||
width: 200px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.logs-block {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,65 @@
|
||||
export class PluginManager {
|
||||
// @ts-ignore
|
||||
map: {
|
||||
[key: string]: any;
|
||||
} = {};
|
||||
|
||||
/**
|
||||
* 初始化plugins
|
||||
* @param plugins
|
||||
*/
|
||||
init(plugins) {
|
||||
const list = plugins;
|
||||
const map = {};
|
||||
for (const plugin of list) {
|
||||
map[plugin.key] = plugin;
|
||||
}
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
get(name: string) {
|
||||
return this.map[name];
|
||||
}
|
||||
|
||||
getPreStepOutputOptions({ pipeline, currentStageIndex, currentStepIndex, currentTask }) {
|
||||
const steps = this.collectionPreStepOutputs({
|
||||
pipeline,
|
||||
currentStageIndex,
|
||||
currentStepIndex,
|
||||
currentTask
|
||||
});
|
||||
const options: any[] = [];
|
||||
for (const step of steps) {
|
||||
const stepDefine = this.get(step.type);
|
||||
for (const key in stepDefine?.output) {
|
||||
options.push({
|
||||
value: `step.${step.id}.${key}`,
|
||||
label: `${stepDefine.output[key].title}【from:${step.title}】`,
|
||||
type: step.type
|
||||
});
|
||||
}
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
collectionPreStepOutputs({ pipeline, currentStageIndex, currentStepIndex, currentTask }) {
|
||||
const steps: any[] = [];
|
||||
// 开始放step
|
||||
for (let i = 0; i < currentStageIndex; i++) {
|
||||
const stage = pipeline.stages[i];
|
||||
for (const task of stage.tasks) {
|
||||
for (const step of task.steps) {
|
||||
steps.push(step);
|
||||
}
|
||||
}
|
||||
}
|
||||
//放当前任务下的step
|
||||
for (let i = 0; i < currentStepIndex; i++) {
|
||||
const step = currentTask.steps[i];
|
||||
steps.push(step);
|
||||
}
|
||||
return steps;
|
||||
}
|
||||
}
|
||||
|
||||
export const pluginManager = new PluginManager();
|
||||
@@ -0,0 +1,22 @@
|
||||
import { PluginDefine, Pipeline } from "@certd/pipeline/src";
|
||||
export * from "@certd/pipeline/src";
|
||||
export type PipelineDetail = {
|
||||
pipeline: Pipeline;
|
||||
};
|
||||
|
||||
export type RunHistory = {
|
||||
id: any;
|
||||
pipeline: Pipeline;
|
||||
logs?: {
|
||||
[id: string]: string[];
|
||||
};
|
||||
};
|
||||
|
||||
export type PipelineOptions = {
|
||||
doTrigger(options: { pipelineId }): Promise<void>;
|
||||
doSave(pipelineConfig: PipelineDefile): Promise<void>;
|
||||
getPipelineDetail(query: { pipelineId }): Promise<PipelineDetail>;
|
||||
getHistoryList(query: { pipelineId }): Promise<RunHistory[]>;
|
||||
getHistoryDetail(query: { historyId }): Promise<RunHistory>;
|
||||
getPlugins(): Promise<PluginDefine[]>;
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
const StatusEnum = {
|
||||
success: {
|
||||
value: "success",
|
||||
label: "成功",
|
||||
color: "green",
|
||||
icon: "ant-design:check-circle-outlined"
|
||||
},
|
||||
error: {
|
||||
value: "error",
|
||||
label: "错误",
|
||||
color: "red",
|
||||
icon: "ant-design:info-circle-outlined"
|
||||
},
|
||||
skip: {
|
||||
value: "skip",
|
||||
label: "跳过",
|
||||
color: "blue",
|
||||
icon: "fluent:arrow-step-over-20-filled"
|
||||
},
|
||||
start: {
|
||||
value: "start",
|
||||
label: "运行中",
|
||||
color: "blue",
|
||||
spin: true,
|
||||
icon: "ant-design:sync-outlined"
|
||||
},
|
||||
none: {
|
||||
value: "none",
|
||||
label: "未运行",
|
||||
color: "blue",
|
||||
icon: "ant-design:minus-circle-twotone"
|
||||
}
|
||||
};
|
||||
export const statusUtil = {
|
||||
getColor(status = "none") {
|
||||
return StatusEnum[status].color;
|
||||
},
|
||||
get(status = "none") {
|
||||
return StatusEnum[status];
|
||||
},
|
||||
|
||||
getOptions() {
|
||||
const options: any[] = [];
|
||||
for (const key of Object.keys(StatusEnum)) {
|
||||
options.push(StatusEnum[key]);
|
||||
}
|
||||
return options;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
import { requestForMock } from "/src/api/service";
|
||||
const request = requestForMock;
|
||||
const apiPrefix = "/mock/AdvancedBigData";
|
||||
export function GetList(query) {
|
||||
return request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "get",
|
||||
data: query
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj) {
|
||||
return request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj) {
|
||||
return request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetObj(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/info",
|
||||
method: "get",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
import * as api from "./api";
|
||||
import { message } from "ant-design-vue";
|
||||
import { dict } from "@fast-crud/fast-crud";
|
||||
export default function ({ expose }) {
|
||||
const pageRequest = async (query) => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }) => {
|
||||
form.id = row.id;
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({ row }) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
|
||||
const addRequest = async ({ form }) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
|
||||
return {
|
||||
output: {},
|
||||
crudOptions: {
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
},
|
||||
table: {
|
||||
scroll: {
|
||||
//启用横向滚动条,设置一个大于所有列宽之和的值,一般大于表格宽度
|
||||
x: 2400
|
||||
}
|
||||
},
|
||||
pagination: {
|
||||
pageSize: 100
|
||||
},
|
||||
rowHandle: {
|
||||
fixed: "right"
|
||||
},
|
||||
columns: {
|
||||
id: {
|
||||
title: "ID",
|
||||
key: "id",
|
||||
type: "number",
|
||||
column: {
|
||||
width: 50
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
text: {
|
||||
title: "文本",
|
||||
type: "text"
|
||||
},
|
||||
dict1: {
|
||||
title: "字典1",
|
||||
type: "dict-select",
|
||||
dict: dict({
|
||||
url: "/mock/dicts/OpenStatusEnum?from=dict1"
|
||||
})
|
||||
},
|
||||
dict2: {
|
||||
title: "字典2",
|
||||
type: "dict-select",
|
||||
dict: dict({
|
||||
url: "/mock/dicts/OpenStatusEnum?from=dict2"
|
||||
})
|
||||
},
|
||||
dict3: {
|
||||
title: "字典3",
|
||||
type: "dict-select",
|
||||
dict: dict({
|
||||
url: "/mock/dicts/OpenStatusEnum?from=dict3"
|
||||
})
|
||||
},
|
||||
dict4: {
|
||||
title: "字典4",
|
||||
type: "dict-select",
|
||||
dict: dict({
|
||||
url: "/mock/dicts/OpenStatusEnum?from=dict4"
|
||||
})
|
||||
},
|
||||
dict5: {
|
||||
title: "字典5",
|
||||
type: "dict-select",
|
||||
dict: dict({
|
||||
url: "/mock/dicts/OpenStatusEnum?from=dict5"
|
||||
})
|
||||
},
|
||||
dict6: {
|
||||
title: "字典6",
|
||||
type: "dict-select",
|
||||
dict: dict({
|
||||
url: "/mock/dicts/OpenStatusEnum?from=dict6"
|
||||
})
|
||||
},
|
||||
dict7: {
|
||||
title: "字典7",
|
||||
type: "dict-select",
|
||||
dict: dict({
|
||||
url: "/mock/dicts/OpenStatusEnum?from=dict7"
|
||||
})
|
||||
},
|
||||
dict8: {
|
||||
title: "字典8",
|
||||
type: "dict-select",
|
||||
dict: dict({
|
||||
url: "/mock/dicts/OpenStatusEnum?from=dict8"
|
||||
})
|
||||
},
|
||||
dict9: {
|
||||
title: "字典9",
|
||||
type: "dict-select",
|
||||
dict: dict({
|
||||
url: "/mock/dicts/OpenStatusEnum?from=dict9"
|
||||
})
|
||||
},
|
||||
dict10: {
|
||||
title: "字典10",
|
||||
type: "dict-select",
|
||||
dict: dict({
|
||||
url: "/mock/dicts/OpenStatusEnum?from=dict10"
|
||||
})
|
||||
},
|
||||
text1: {
|
||||
title: "文本",
|
||||
type: "text"
|
||||
},
|
||||
text2: {
|
||||
title: "文本",
|
||||
type: "text"
|
||||
},
|
||||
text3: {
|
||||
title: "文本",
|
||||
type: "text"
|
||||
},
|
||||
text4: {
|
||||
title: "文本",
|
||||
type: "text"
|
||||
},
|
||||
text5: {
|
||||
title: "文本",
|
||||
type: "text"
|
||||
},
|
||||
text6: {
|
||||
title: "文本",
|
||||
type: "text"
|
||||
},
|
||||
text7: {
|
||||
title: "文本",
|
||||
type: "text"
|
||||
},
|
||||
text8: {
|
||||
title: "文本",
|
||||
type: "text"
|
||||
},
|
||||
text9: {
|
||||
title: "文本",
|
||||
type: "text"
|
||||
},
|
||||
text10: {
|
||||
title: "文本",
|
||||
type: "text"
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<fs-page>
|
||||
<template #header>
|
||||
<div class="title">大量数据</div>
|
||||
</template>
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted } from "vue";
|
||||
import { useCrud, useExpose } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
export default defineComponent({
|
||||
name: "AdvancedBigData",
|
||||
setup() {
|
||||
// crud组件的ref
|
||||
const crudRef = ref();
|
||||
// crud 配置的ref
|
||||
const crudBinding = ref();
|
||||
// 暴露的方法
|
||||
const { expose } = useExpose({ crudRef, crudBinding });
|
||||
// 你的crud配置
|
||||
const { crudOptions, output } = createCrudOptions({ expose });
|
||||
// 初始化crud配置
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-unused-vars
|
||||
const { resetCrudOptions } = useCrud({ expose, crudOptions });
|
||||
// 你可以调用此方法,重新初始化crud配置
|
||||
// resetCrudOptions(options)
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
expose.doRefresh();
|
||||
});
|
||||
|
||||
return {
|
||||
crudBinding,
|
||||
crudRef,
|
||||
...output
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,126 @@
|
||||
import mockUtil from "/src/mock/base";
|
||||
const options = {
|
||||
name: "AdvancedBigData",
|
||||
idGenerator: 0,
|
||||
copyTimes: 1000
|
||||
};
|
||||
const list = [
|
||||
{
|
||||
text: "测试文本",
|
||||
dict1: "1",
|
||||
dict2: "1",
|
||||
dict3: "2",
|
||||
dict4: "1",
|
||||
dict5: "2",
|
||||
dict6: "1",
|
||||
dict7: "1",
|
||||
dict8: "1",
|
||||
text1: "测试文本1",
|
||||
text2: "测试文本2",
|
||||
text3: "测试文本3",
|
||||
text4: "测试文本4",
|
||||
text5: "测试文本5",
|
||||
text6: "测试文本6",
|
||||
text7: "测试文本7",
|
||||
text8: "测试文本8",
|
||||
dict9: "2",
|
||||
dict10: "1",
|
||||
dict11: "2",
|
||||
dict12: "1"
|
||||
},
|
||||
{
|
||||
text: "测试文本",
|
||||
dict1: "1",
|
||||
dict2: "1",
|
||||
dict3: "2",
|
||||
dict4: "1",
|
||||
dict5: "2",
|
||||
dict6: "1",
|
||||
dict7: "1",
|
||||
dict8: "1",
|
||||
text1: "测试文本1",
|
||||
text2: "测试文本2",
|
||||
text3: "测试文本3",
|
||||
text4: "测试文本4",
|
||||
text5: "测试文本5",
|
||||
text6: "测试文本6",
|
||||
text7: "测试文本7",
|
||||
text8: "测试文本8",
|
||||
dict9: "2",
|
||||
dict10: "1",
|
||||
dict11: "2",
|
||||
dict12: "1"
|
||||
},
|
||||
{
|
||||
text: "测试文本",
|
||||
dict1: "1",
|
||||
dict2: "1",
|
||||
dict3: "2",
|
||||
dict4: "1",
|
||||
dict5: "2",
|
||||
dict6: "1",
|
||||
dict7: "1",
|
||||
dict8: "1",
|
||||
text1: "测试文本1",
|
||||
text2: "测试文本2",
|
||||
text3: "测试文本3",
|
||||
text4: "测试文本4",
|
||||
text5: "测试文本5",
|
||||
text6: "测试文本6",
|
||||
text7: "测试文本7",
|
||||
text8: "测试文本8",
|
||||
dict9: "2",
|
||||
dict10: "1",
|
||||
dict11: "2",
|
||||
dict12: "1"
|
||||
},
|
||||
{
|
||||
text: "测试文本",
|
||||
dict1: "1",
|
||||
dict2: "1",
|
||||
dict3: "2",
|
||||
dict4: "1",
|
||||
dict5: "2",
|
||||
dict6: "1",
|
||||
dict7: "1",
|
||||
dict8: "1",
|
||||
text1: "测试文本1",
|
||||
text2: "测试文本2",
|
||||
text3: "测试文本3",
|
||||
text4: "测试文本4",
|
||||
text5: "测试文本5",
|
||||
text6: "测试文本6",
|
||||
text7: "测试文本7",
|
||||
text8: "测试文本8",
|
||||
dict9: "2",
|
||||
dict10: "1",
|
||||
dict11: "2",
|
||||
dict12: "1"
|
||||
},
|
||||
{
|
||||
text: "测试文本",
|
||||
dict1: "1",
|
||||
dict2: "1",
|
||||
dict3: "2",
|
||||
dict4: "1",
|
||||
dict5: "2",
|
||||
dict6: "1",
|
||||
dict7: "1",
|
||||
dict8: "1",
|
||||
text1: "测试文本1",
|
||||
text2: "测试文本2",
|
||||
text3: "测试文本3",
|
||||
text4: "测试文本4",
|
||||
text5: "测试文本5",
|
||||
text6: "测试文本6",
|
||||
text7: "测试文本7",
|
||||
text8: "测试文本8",
|
||||
dict9: "2",
|
||||
dict10: "1",
|
||||
dict11: "2",
|
||||
dict12: "1"
|
||||
}
|
||||
];
|
||||
options.list = list;
|
||||
const mock = mockUtil.buildMock(options);
|
||||
export default mock;
|
||||
@@ -0,0 +1,48 @@
|
||||
import { requestForMock } from "/src/api/service";
|
||||
const request = requestForMock;
|
||||
const apiPrefix = "/mock/AdvancedFromBackend";
|
||||
export function GetList(query) {
|
||||
return request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "get",
|
||||
data: query
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj) {
|
||||
return request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj) {
|
||||
return request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetObj(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/info",
|
||||
method: "get",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
export function GetCrud() {
|
||||
return request({
|
||||
url: apiPrefix + "/crud",
|
||||
method: "get"
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
export const crudOptions = `
|
||||
({expose,dict}) => {
|
||||
return {
|
||||
columns: {
|
||||
id: {
|
||||
title: "ID",
|
||||
key: "id",
|
||||
type: "number",
|
||||
column: {
|
||||
width: 50
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
radio: {
|
||||
title: "状态",
|
||||
search: { show: true },
|
||||
type: "dict-radio",
|
||||
dict: dict({
|
||||
url: "/mock/dicts/OpenStatusEnum?single"
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
`;
|
||||
@@ -0,0 +1,27 @@
|
||||
import * as api from "./api";
|
||||
export default function ({ expose }) {
|
||||
const pageRequest = async (query) => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }) => {
|
||||
form.id = row.id;
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({ row }) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
|
||||
const addRequest = async ({ form }) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
return {
|
||||
crudOptions: {
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<fs-crud v-if="crudBinding" ref="crudRef" v-bind="crudBinding" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted } from "vue";
|
||||
import { useCrud, dict, useExpose } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
import { GetCrud } from "./api";
|
||||
import _ from "lodash-es";
|
||||
export default defineComponent({
|
||||
name: "AdvancedFromBackend",
|
||||
setup() {
|
||||
// crud组件的ref
|
||||
const crudRef = ref();
|
||||
// crud 配置的ref
|
||||
const crudBinding = ref();
|
||||
// 暴露的方法
|
||||
const { expose } = useExpose({ crudRef, crudBinding });
|
||||
// 你的crud配置
|
||||
const { crudOptions } = createCrudOptions({ expose });
|
||||
// 初始化crud配置
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(async () => {
|
||||
// 从后台获取crud配置
|
||||
const ret = await GetCrud();
|
||||
// 编译
|
||||
const crudBackend = eval(ret);
|
||||
// 本示例返回的是一个方法字符串,所以要先执行这个方法,获取options
|
||||
const crudOptionsFromBackend = crudBackend({ expose, dict });
|
||||
// 与本地options合并
|
||||
_.merge(crudOptions, crudOptionsFromBackend);
|
||||
// useCrud
|
||||
useCrud({ expose, crudOptions });
|
||||
// 刷新数据
|
||||
expose.doRefresh();
|
||||
});
|
||||
|
||||
return {
|
||||
crudBinding,
|
||||
crudRef
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,35 @@
|
||||
import mockUtil from "/src/mock/base";
|
||||
import { crudOptions } from "./crud-backend";
|
||||
const options = {
|
||||
name: "AdvancedFromBackend",
|
||||
idGenerator: 0
|
||||
};
|
||||
const list = [
|
||||
{
|
||||
radio: "1"
|
||||
},
|
||||
{
|
||||
radio: "2"
|
||||
},
|
||||
{
|
||||
radio: "0"
|
||||
}
|
||||
];
|
||||
|
||||
options.list = list;
|
||||
options.copyTimes = 1000;
|
||||
const mock = mockUtil.buildMock(options);
|
||||
|
||||
mock.push({
|
||||
path: "/AdvancedFromBackend/crud",
|
||||
method: "get",
|
||||
handle(req) {
|
||||
return {
|
||||
code: 0,
|
||||
msg: "success",
|
||||
data: crudOptions
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
export default mock;
|
||||
@@ -0,0 +1,42 @@
|
||||
import { requestForMock } from "/src/api/service";
|
||||
const request = requestForMock;
|
||||
const apiPrefix = "/mock/AdvancedInDialog";
|
||||
export function GetList(query) {
|
||||
return request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "get",
|
||||
data: query
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj) {
|
||||
return request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj) {
|
||||
return request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetObj(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/info",
|
||||
method: "get",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
import * as api from "./api";
|
||||
export default function ({ expose }) {
|
||||
const pageRequest = async (query) => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }) => {
|
||||
form.id = row.id;
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({ row }) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
|
||||
const addRequest = async ({ form }) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
return {
|
||||
crudOptions: {
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
},
|
||||
columns: {
|
||||
name: {
|
||||
title: "姓名",
|
||||
type: "text", //虽然不写也能正确显示组件,但不建议省略它
|
||||
search: { show: true },
|
||||
form: {
|
||||
component: {
|
||||
maxlength: 20
|
||||
}
|
||||
}
|
||||
},
|
||||
search: {
|
||||
title: "搜索",
|
||||
type: "text",
|
||||
form: {
|
||||
component: {
|
||||
addonAfter: "后置",
|
||||
suffix: "suffix",
|
||||
children: {
|
||||
addonBefore() {
|
||||
return <SearchOutlined />;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
password: {
|
||||
title: "密码",
|
||||
type: "password",
|
||||
column: {
|
||||
//一般密码不显示在列里面
|
||||
show: false
|
||||
}
|
||||
},
|
||||
intro: {
|
||||
title: "简介",
|
||||
type: "textarea",
|
||||
form: {
|
||||
component: { showWordLimit: true, maxlength: 200 }
|
||||
},
|
||||
column: {
|
||||
ellipsis: true
|
||||
}
|
||||
},
|
||||
render: {
|
||||
title: "复杂输入(render)",
|
||||
form: {
|
||||
title: "复杂输入",
|
||||
component: {
|
||||
render(context) {
|
||||
console.log("context scope", context);
|
||||
return (
|
||||
<a-input-group compact>
|
||||
<a-input
|
||||
placeholder={"render1 input"}
|
||||
style="width: 50%"
|
||||
v-model={[context.form.render, "value"]}
|
||||
/>
|
||||
<a-input
|
||||
placeholder={"render2 input"}
|
||||
style="width: 50%"
|
||||
v-model={[context.form.render2, "value"]}
|
||||
/>
|
||||
</a-input-group>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
render2: {
|
||||
title: "我的值是由复杂输入列输入的",
|
||||
column: {
|
||||
width: "300px"
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<fs-page>
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding" />
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted } from "vue";
|
||||
import { useCrud } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
import { useExpose } from "@fast-crud/fast-crud";
|
||||
export default defineComponent({
|
||||
name: "fs-in-dialog",
|
||||
setup() {
|
||||
// crud组件的ref
|
||||
const crudRef = ref();
|
||||
// crud 配置的ref
|
||||
const crudBinding = ref();
|
||||
// 暴露的方法
|
||||
const { expose } = useExpose({ crudRef, crudBinding });
|
||||
// 你的crud配置
|
||||
const { crudOptions } = createCrudOptions({ expose });
|
||||
// 初始化crud配置
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-unused-vars
|
||||
const { resetCrudOptions } = useCrud({ expose, crudOptions });
|
||||
// 你可以调用此方法,重新初始化crud配置
|
||||
// resetCrudOptions(options)
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
expose.doRefresh();
|
||||
});
|
||||
|
||||
return {
|
||||
crudBinding,
|
||||
crudRef
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,40 @@
|
||||
import mockUtil from "/src/mock/base";
|
||||
const options = {
|
||||
name: "AdvancedInDialog",
|
||||
idGenerator: 0
|
||||
};
|
||||
const list = [
|
||||
{
|
||||
name: "王小虎",
|
||||
date: "2016-05-02",
|
||||
status: "0",
|
||||
province: "1",
|
||||
avatar: "https://alicdn.antdv.com/vue.png",
|
||||
show: true,
|
||||
city: "sz",
|
||||
address: "123123",
|
||||
zip: "518000",
|
||||
intro: "王小虎是element-plus的table示例出现的名字"
|
||||
},
|
||||
{
|
||||
name: "张三",
|
||||
date: "2016-05-04",
|
||||
status: "1",
|
||||
province: "2"
|
||||
},
|
||||
{
|
||||
name: "李四",
|
||||
date: 2232433534511,
|
||||
status: "1",
|
||||
province: "0"
|
||||
},
|
||||
{
|
||||
name: "王五",
|
||||
date: "2016-05-03",
|
||||
status: "2",
|
||||
province: "wh,gz"
|
||||
}
|
||||
];
|
||||
options.list = list;
|
||||
const mock = mockUtil.buildMock(options);
|
||||
export default mock;
|
||||
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<fs-page>
|
||||
<template #header>
|
||||
<div class="title">
|
||||
对话框中显示crud
|
||||
</div>
|
||||
</template>
|
||||
<div style="padding:50px">
|
||||
<a-button type="primary" @click="openDialog">打开对话框</a-button>
|
||||
</div>
|
||||
|
||||
<a-modal v-model:visible="dialogShow" width="80%" title="fs-crud in dialog">
|
||||
<div style="height: 400px;position: relative">
|
||||
<fs-in-dialog ></fs-in-dialog>
|
||||
</div>
|
||||
</a-modal>
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref } from "vue";
|
||||
import FsInDialog from './crud/index.vue'
|
||||
export default defineComponent({
|
||||
name: "InDialog",
|
||||
components:{FsInDialog},
|
||||
setup() {
|
||||
const dialogShow = ref(false)
|
||||
function openDialog(){
|
||||
dialogShow.value=true
|
||||
}
|
||||
return {
|
||||
dialogShow,
|
||||
openDialog
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,42 @@
|
||||
import { requestForMock } from "/src/api/service";
|
||||
const request = requestForMock;
|
||||
const apiPrefix = "/mock/FormLinkage";
|
||||
export function GetList(query) {
|
||||
return request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "get",
|
||||
data: query
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj) {
|
||||
return request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj) {
|
||||
return request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetObj(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/info",
|
||||
method: "get",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
import * as api from "./api";
|
||||
import { dict } from "@fast-crud/fast-crud";
|
||||
export default function ({ expose }) {
|
||||
const pageRequest = async (query) => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }) => {
|
||||
form.id = row.id;
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({ row }) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
|
||||
const addRequest = async ({ form }) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
|
||||
return {
|
||||
crudOptions: {
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
},
|
||||
rowHandle: {
|
||||
align: "center"
|
||||
},
|
||||
columns: {
|
||||
id: {
|
||||
title: "ID",
|
||||
key: "id",
|
||||
type: "number",
|
||||
column: {
|
||||
width: 50
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
province: {
|
||||
title: "省",
|
||||
type: "dict-select",
|
||||
search: {
|
||||
show: true
|
||||
},
|
||||
dict: dict({
|
||||
url: "/mock/linkage/province",
|
||||
value: "id",
|
||||
cache: true
|
||||
}),
|
||||
form: {
|
||||
valueChange({ form, value, getComponentRef }) {
|
||||
form.city = undefined; // 将“city”的值置空
|
||||
form.county = undefined; // 将“county”的值置空
|
||||
if (value) {
|
||||
getComponentRef("city").reloadDict(); // 执行city的select组件的reloadDict()方法,触发“city”重新加载字典
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
city: {
|
||||
title: "市",
|
||||
type: "dict-select",
|
||||
search: {
|
||||
show: true
|
||||
},
|
||||
dict: dict({
|
||||
cache: true,
|
||||
prototype: true,
|
||||
// url() 改成构建url,返回一个url
|
||||
url({ form }) {
|
||||
if (form && form.province != null) {
|
||||
// 本数据字典的url是通过前一个select的选项决定的
|
||||
return `/mock/linkage/city?province=${form.province}`;
|
||||
}
|
||||
return undefined; // 返回undefined 将不加载字典
|
||||
},
|
||||
value: "id"
|
||||
}),
|
||||
form: {
|
||||
// 注释同上
|
||||
valueChange({ value, form, getComponentRef }) {
|
||||
if (value) {
|
||||
form.county = undefined; // 将county的value置空
|
||||
const countySelect = getComponentRef("county");
|
||||
if (form && form.province && form.city) {
|
||||
countySelect.reloadDict(); // 重新加载字典项
|
||||
} else {
|
||||
countySelect.clearDict(); // 清空选项
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
county: {
|
||||
title: "区",
|
||||
type: "dict-select",
|
||||
search: {
|
||||
show: true
|
||||
},
|
||||
dict: dict({
|
||||
value: "id",
|
||||
cache: true,
|
||||
prototype: true,
|
||||
url({ form }) {
|
||||
if (form && form.province != null && form.city != null) {
|
||||
return `/mock/linkage/county?province=${form.province} &city=${form.city}`;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted } from "vue";
|
||||
import { useCrud } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
import { useExpose } from "@fast-crud/fast-crud";
|
||||
export default defineComponent({
|
||||
name: "FormLinkage",
|
||||
setup() {
|
||||
// crud组件的ref
|
||||
const crudRef = ref();
|
||||
// crud 配置的ref
|
||||
const crudBinding = ref();
|
||||
// 暴露的方法
|
||||
const { expose } = useExpose({ crudRef, crudBinding });
|
||||
// 你的crud配置
|
||||
const { crudOptions } = createCrudOptions({ expose });
|
||||
// 初始化crud配置
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-unused-vars
|
||||
const { resetCrudOptions } = useCrud({ expose, crudOptions });
|
||||
// 你可以调用此方法,重新初始化crud配置
|
||||
// resetCrudOptions(options)
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
expose.doRefresh();
|
||||
});
|
||||
|
||||
return {
|
||||
crudBinding,
|
||||
crudRef
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,129 @@
|
||||
import mockUtil from "/src/mock/base";
|
||||
import _ from "lodash-es";
|
||||
const options = {
|
||||
name: "FormLinkage",
|
||||
idGenerator: 0
|
||||
};
|
||||
const list = [
|
||||
{
|
||||
province: 10000,
|
||||
city: 100003,
|
||||
county: 100004
|
||||
},
|
||||
{
|
||||
province: 10010,
|
||||
city: 100113,
|
||||
county: 100115
|
||||
}
|
||||
];
|
||||
const tree = [
|
||||
{
|
||||
id: 10000,
|
||||
label: "北京市",
|
||||
children: [
|
||||
{
|
||||
id: 100003,
|
||||
label: "北京市辖区",
|
||||
children: [
|
||||
{ id: 100004, label: "东城区" },
|
||||
{ id: 100005, label: "西城区" }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 100103,
|
||||
label: "北京郊区",
|
||||
children: [
|
||||
{ id: 100104, label: "东郊" },
|
||||
{ id: 100105, label: "西郊" }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 10010,
|
||||
label: "天津市",
|
||||
children: [
|
||||
{
|
||||
id: 100013,
|
||||
label: "天津市辖区",
|
||||
children: [
|
||||
{ id: 100014, label: "天津湾" },
|
||||
{ id: 100015, label: "渤海湾" }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 100113,
|
||||
label: "天津市郊区",
|
||||
children: [
|
||||
{ id: 100114, label: "天津湾郊区" },
|
||||
{ id: 100115, label: "渤海湾郊区" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
options.list = list;
|
||||
options.copyTimes = 1000;
|
||||
const mock = mockUtil.buildMock(options);
|
||||
|
||||
function omitChildren(orignalListt) {
|
||||
const list = [];
|
||||
orignalListt.forEach((item) => {
|
||||
list.push(_.omit(item, "children"));
|
||||
});
|
||||
return list;
|
||||
}
|
||||
mock.push({
|
||||
path: "/mock/linkage/province",
|
||||
method: "get",
|
||||
handle() {
|
||||
const list = omitChildren(tree);
|
||||
return {
|
||||
code: 0,
|
||||
msg: "success",
|
||||
data: list
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
mock.push({
|
||||
path: "/mock/linkage/city",
|
||||
method: "get",
|
||||
handle(req) {
|
||||
const province = parseInt(req.params.province);
|
||||
const a = tree.filter((item) => {
|
||||
return item.id === province;
|
||||
});
|
||||
const list = omitChildren(a[0].children);
|
||||
return {
|
||||
code: 0,
|
||||
msg: "success",
|
||||
data: list
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
mock.push({
|
||||
path: "/mock/linkage/county",
|
||||
method: "get",
|
||||
handle(req) {
|
||||
const province = parseInt(req.params.province);
|
||||
const a = tree.filter((item) => {
|
||||
return item.id === province;
|
||||
});
|
||||
const city = parseInt(req.params.city);
|
||||
const b = a[0].children.filter((item) => {
|
||||
return item.id === city;
|
||||
});
|
||||
|
||||
const list = omitChildren(b[0].children);
|
||||
return {
|
||||
code: 0,
|
||||
msg: "success",
|
||||
data: list
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
export default mock;
|
||||
@@ -0,0 +1,42 @@
|
||||
import { requestForMock } from "/src/api/service";
|
||||
const request = requestForMock;
|
||||
const apiPrefix = "/mock/AdvancedLocalPagination";
|
||||
export function GetList(query) {
|
||||
return request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "get",
|
||||
data: query
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj) {
|
||||
return request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj) {
|
||||
return request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetObj(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/info",
|
||||
method: "get",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
import * as api from "./api";
|
||||
import _ from "lodash-es";
|
||||
import { dict } from "@fast-crud/fast-crud";
|
||||
export default function ({ expose, localDataRef }) {
|
||||
const pageRequest = async ({ page, query }) => {
|
||||
//总数据
|
||||
let data = localDataRef.value;
|
||||
//获取请求参数
|
||||
const limit = page.limit;
|
||||
let offset = page.offset;
|
||||
data = data.filter((item) => {
|
||||
// 根据你的业务,编写你的本地查询逻辑
|
||||
// text改成你的查询字段
|
||||
if (query.status && item.status !== query.status) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// 本地分页
|
||||
const start = offset;
|
||||
let end = offset + limit;
|
||||
if (data.length < end) {
|
||||
end = data.length;
|
||||
}
|
||||
const records = data.slice(start, end);
|
||||
|
||||
// 构造返回结果
|
||||
return {
|
||||
offset,
|
||||
limit,
|
||||
total: localDataRef.value.length,
|
||||
records
|
||||
};
|
||||
};
|
||||
const editRequest = async ({ form, row }) => {
|
||||
form.id = row.id;
|
||||
await api.UpdateObj(form);
|
||||
//更新本地数据
|
||||
const tableData = localDataRef.value;
|
||||
for (const item of tableData) {
|
||||
if (item.id === form.id) {
|
||||
_.merge(item, form);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const addRequest = async ({ form }) => {
|
||||
const id = await api.AddObj(form);
|
||||
//本地添加
|
||||
form.id = id;
|
||||
localDataRef.value.unshift(form);
|
||||
return id;
|
||||
};
|
||||
|
||||
const delRequest = async ({ row }) => {
|
||||
await api.DelObj(row.id);
|
||||
//本地删除那一条记录
|
||||
const tableData = localDataRef.value;
|
||||
let index = 0;
|
||||
for (const item of tableData) {
|
||||
if (item.id === row.id) {
|
||||
localDataRef.value.splice(index, 1);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
output: {},
|
||||
crudOptions: {
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
},
|
||||
columns: {
|
||||
id: {
|
||||
title: "ID",
|
||||
key: "id",
|
||||
type: "number",
|
||||
column: {
|
||||
width: 50
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
status: {
|
||||
title: "状态",
|
||||
search: { show: true },
|
||||
type: "dict-select",
|
||||
dict: dict({
|
||||
url: "/mock/dicts/OpenStatusEnum?single"
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<fs-page>
|
||||
<template #header>
|
||||
<div class="title">本地分页</div>
|
||||
</template>
|
||||
<fs-crud v-if="crudBinding" ref="crudRef" v-bind="crudBinding">
|
||||
<template #actionbar-right>
|
||||
<a-alert type="warning" class="ml-1" message="先从后台获取全部数据,然后本地分页展示" />
|
||||
</template>
|
||||
</fs-crud>
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted } from "vue";
|
||||
import { useCrud, useExpose } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
import { GetList } from "./api";
|
||||
|
||||
/**
|
||||
* 本示例演示如何本地分页
|
||||
* 主要就是将pageRequest修改为从本地获取数据就行了
|
||||
*/
|
||||
export default defineComponent({
|
||||
name: "AdvanceLocalPagination",
|
||||
setup() {
|
||||
// crud组件的ref
|
||||
const crudRef = ref();
|
||||
// crud 配置的ref
|
||||
const crudBinding = ref();
|
||||
|
||||
const localDataRef = ref();
|
||||
|
||||
onMounted(async () => {
|
||||
//先加载后台数据
|
||||
const ret = await GetList({ page: { offset: 0, limit: 99999999 }, query: {}, sort: {} });
|
||||
localDataRef.value = ret.records;
|
||||
|
||||
//然后再初始化crud
|
||||
// 暴露的方法
|
||||
const { expose } = useExpose({ crudRef, crudBinding });
|
||||
// 你的crud配置
|
||||
const { crudOptions } = createCrudOptions({ expose, localDataRef });
|
||||
// 初始化crud配置
|
||||
useCrud({ expose, crudOptions });
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
await expose.doRefresh();
|
||||
});
|
||||
|
||||
return {
|
||||
crudBinding,
|
||||
crudRef
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,19 @@
|
||||
import mockUtil from "/src/mock/base";
|
||||
const options = {
|
||||
name: "AdvancedLocalPagination",
|
||||
idGenerator: 0
|
||||
};
|
||||
const list = [
|
||||
{
|
||||
status: "1"
|
||||
},
|
||||
{
|
||||
status: "2"
|
||||
},
|
||||
{
|
||||
status: "0"
|
||||
}
|
||||
];
|
||||
options.list = list;
|
||||
const mock = mockUtil.buildMock(options);
|
||||
export default mock;
|
||||
@@ -0,0 +1,50 @@
|
||||
import { requestForMock } from "/src/api/service";
|
||||
const request = requestForMock;
|
||||
const apiPrefix = "/mock/AdvancedNest";
|
||||
export function GetList(query) {
|
||||
return request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "get",
|
||||
data: query
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj) {
|
||||
return request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj) {
|
||||
return request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetObj(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/info",
|
||||
method: "get",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function BatchDelete(ids) {
|
||||
return request({
|
||||
url: apiPrefix + "/batchDelete",
|
||||
method: "post",
|
||||
data: { ids }
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import { requestForMock } from "/src/api/service";
|
||||
const request = requestForMock;
|
||||
const apiPrefix = "/mock/AdvancedAside";
|
||||
export function GetList(query) {
|
||||
return request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "get",
|
||||
data: query
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj) {
|
||||
return request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj) {
|
||||
return request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetObj(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/info",
|
||||
method: "get",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function BatchDelete(ids) {
|
||||
return request({
|
||||
url: apiPrefix + "/batchDelete",
|
||||
method: "post",
|
||||
data: { ids }
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import * as api from "./api";
|
||||
import { dict } from "@fast-crud/fast-crud";
|
||||
export default function ({ expose }) {
|
||||
const editRequest = async ({ form, row }) => {
|
||||
form.id = row.id;
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({ row }) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
const addRequest = async ({ form }) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
|
||||
return {
|
||||
crudOptions: {
|
||||
pagination: {
|
||||
showSizeChanger: false, // antdv
|
||||
showQuickJumper: false // antdv
|
||||
},
|
||||
request: {
|
||||
pageRequest: api.GetList,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
},
|
||||
toolbar: {
|
||||
compact: false
|
||||
},
|
||||
rowHandle: {
|
||||
width: "230px"
|
||||
},
|
||||
table: {},
|
||||
columns: {
|
||||
gradeId: {
|
||||
title: "年级Id",
|
||||
search: { show: true },
|
||||
type: "number",
|
||||
column: {
|
||||
width: 80,
|
||||
align: "center",
|
||||
sortable: true
|
||||
}
|
||||
},
|
||||
class: {
|
||||
title: "班级",
|
||||
search: { show: false },
|
||||
type: "text",
|
||||
column: {
|
||||
sortable: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding">
|
||||
<template #actionbar-right>
|
||||
<a-alert type="warning" class="ml-1" message="左侧表格点击行可以触发这里的查询" />
|
||||
</template>
|
||||
</fs-crud>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted } from "vue";
|
||||
import createCrudOptions from "./crud";
|
||||
import { useExpose, useCrud } from "@fast-crud/fast-crud";
|
||||
export default defineComponent({
|
||||
name: "AsideTable",
|
||||
setup() {
|
||||
// crud组件的ref
|
||||
const crudRef = ref();
|
||||
// crud 配置的ref
|
||||
const crudBinding = ref();
|
||||
// 暴露的方法
|
||||
const { expose } = useExpose({ crudRef, crudBinding });
|
||||
// 你的crud配置
|
||||
const { crudOptions, selectedIds } = createCrudOptions({ expose });
|
||||
// 初始化crud配置
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-unused-vars
|
||||
const { resetCrudOptions } = useCrud({ expose, crudOptions });
|
||||
// 你可以调用此方法,重新初始化crud配置
|
||||
// resetCrudOptions(options)
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
expose.doRefresh();
|
||||
});
|
||||
|
||||
return {
|
||||
crudBinding,
|
||||
crudRef,
|
||||
setSearchFormData: expose.setSearchFormData,
|
||||
doRefresh: expose.doRefresh
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,26 @@
|
||||
import mockUtil from "/src/mock/base";
|
||||
const options = {
|
||||
name: "AdvancedAside",
|
||||
idGenerator: 0
|
||||
};
|
||||
const list = [
|
||||
{
|
||||
class: "一班",
|
||||
gradeId: 1
|
||||
},
|
||||
{
|
||||
class: "二班",
|
||||
gradeId: 1
|
||||
},
|
||||
{
|
||||
class: "三班",
|
||||
gradeId: 2
|
||||
},
|
||||
{
|
||||
class: "四班",
|
||||
gradeId: 2
|
||||
}
|
||||
];
|
||||
options.list = list;
|
||||
const mock = mockUtil.buildMock(options);
|
||||
export default mock;
|
||||
@@ -0,0 +1,101 @@
|
||||
import * as api from "./api";
|
||||
import { ref, shallowRef } from "vue";
|
||||
import SubTable from "./sub-table/index.vue";
|
||||
import { compute } from "@fast-crud/fast-crud";
|
||||
export default function ({ expose, asideTableRef }) {
|
||||
const editRequest = async ({ form, row }) => {
|
||||
form.id = row.id;
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({ row }) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
const addRequest = async ({ form }) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
const currentRow = ref();
|
||||
|
||||
const onCurrentRowChange = (id) => {
|
||||
currentRow.value = id;
|
||||
asideTableRef.value.setSearchFormData({ form: { gradeId: id } });
|
||||
asideTableRef.value.doRefresh();
|
||||
};
|
||||
return {
|
||||
crudOptions: {
|
||||
table: {
|
||||
customRow(record, index) {
|
||||
const clazz = record.id === currentRow.value ? "fs-current-row" : "";
|
||||
return {
|
||||
onClick() {
|
||||
onCurrentRowChange(record.id);
|
||||
},
|
||||
class: clazz
|
||||
};
|
||||
}
|
||||
},
|
||||
pagination: {
|
||||
showSizeChanger: false, // antdv
|
||||
showQuickJumper: false // antdv
|
||||
},
|
||||
form: {
|
||||
wrapper: {
|
||||
is: "a-drawer"
|
||||
}
|
||||
},
|
||||
request: {
|
||||
pageRequest: api.GetList,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
},
|
||||
rowHandle: {
|
||||
width: "240px"
|
||||
},
|
||||
toolbar: {
|
||||
compact: false
|
||||
},
|
||||
columns: {
|
||||
id: {
|
||||
title: "ID",
|
||||
key: "id",
|
||||
type: "number",
|
||||
column: {
|
||||
width: 50
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
grade: {
|
||||
title: "年级",
|
||||
search: { show: true },
|
||||
type: "text",
|
||||
column: {
|
||||
sortable: true
|
||||
}
|
||||
},
|
||||
nestId: {
|
||||
title: "嵌套表格",
|
||||
//复合字段类型
|
||||
type: ["number", "colspan"],
|
||||
form: {
|
||||
// 嵌套表格字段
|
||||
rules: [{ required: true, message: "请选择用户" }],
|
||||
component: {
|
||||
//局部引用子表格,要用shallowRef包裹
|
||||
name: shallowRef(SubTable),
|
||||
vModel: "modelValue",
|
||||
gradeId: compute(({ form }) => {
|
||||
return form.id;
|
||||
})
|
||||
}
|
||||
// antdv 的跨列配置,需要配置如下三个, 可以通过colspan简化
|
||||
// col: { span: 24 },
|
||||
// labelCol: { span: 2 },
|
||||
// wrapperCol: { span: 21 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<a-row class="demo-nest" :gutter="0">
|
||||
<a-col :span="12">
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding">
|
||||
<template #actionbar-right>
|
||||
<a-alert type="warning" class="ml-1" message="<--对话框内嵌套子表格" />
|
||||
</template>
|
||||
</fs-crud>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<aside-table ref="asideTableRef" />
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted } from "vue";
|
||||
import createCrudOptions from "./crud";
|
||||
import { useExpose, useCrud } from "@fast-crud/fast-crud";
|
||||
import AsideTable from "./aside-table/index.vue";
|
||||
export default defineComponent({
|
||||
name: "FeatureNest",
|
||||
// eslint-disable-next-line vue/no-unused-components
|
||||
components: { AsideTable },
|
||||
setup() {
|
||||
// crud组件的ref
|
||||
const crudRef = ref();
|
||||
// crud 配置的ref
|
||||
const crudBinding = ref();
|
||||
// 暴露的方法
|
||||
const { expose } = useExpose({ crudRef, crudBinding });
|
||||
// 你的crud配置
|
||||
const asideTableRef = ref();
|
||||
const { crudOptions } = createCrudOptions({ expose, asideTableRef });
|
||||
// 初始化crud配置
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-unused-vars
|
||||
const { resetCrudOptions } = useCrud({ expose, crudOptions });
|
||||
// 你可以调用此方法,重新初始化crud配置
|
||||
// resetCrudOptions(options)
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
expose.doRefresh();
|
||||
});
|
||||
|
||||
return {
|
||||
crudBinding,
|
||||
crudRef,
|
||||
asideTableRef
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="less">
|
||||
.demo-nest {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,22 @@
|
||||
import mockUtil from "/src/mock/base";
|
||||
const options = {
|
||||
name: "AdvancedNest",
|
||||
idGenerator: 0
|
||||
};
|
||||
const list = [
|
||||
{
|
||||
grade: "一年级",
|
||||
nestId: 1
|
||||
},
|
||||
{
|
||||
grade: "二年级",
|
||||
nestId: 2
|
||||
},
|
||||
{
|
||||
grade: "三年级",
|
||||
nestId: 3
|
||||
}
|
||||
];
|
||||
options.list = list;
|
||||
const mock = mockUtil.buildMock(options);
|
||||
export default mock;
|
||||
@@ -0,0 +1,50 @@
|
||||
import { requestForMock } from "/src/api/service";
|
||||
const request = requestForMock;
|
||||
const apiPrefix = "/mock/AdvancedSubTable";
|
||||
export function GetList(query) {
|
||||
return request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "get",
|
||||
data: query
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj) {
|
||||
return request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj) {
|
||||
return request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetObj(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/info",
|
||||
method: "get",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function BatchDelete(ids) {
|
||||
return request({
|
||||
url: apiPrefix + "/batchDelete",
|
||||
method: "post",
|
||||
data: { ids }
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import * as api from "./api";
|
||||
export default function ({ expose, props, ctx }) {
|
||||
const editRequest = async ({ form, row }) => {
|
||||
form.id = row.id;
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({ row }) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
const addRequest = async ({ form }) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
|
||||
return {
|
||||
crudOptions: {
|
||||
table: {
|
||||
customRow(record, index) {
|
||||
const clazz = record.id === props.modelValue ? "fs-current-row" : "";
|
||||
return {
|
||||
onClick() {
|
||||
ctx.emit("update:modelValue", record.id);
|
||||
},
|
||||
class: clazz
|
||||
};
|
||||
}
|
||||
},
|
||||
request: {
|
||||
pageRequest: api.GetList,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
},
|
||||
search: { show: false },
|
||||
form: {
|
||||
wrapper: {
|
||||
is: "a-drawer"
|
||||
}
|
||||
},
|
||||
columns: {
|
||||
id: {
|
||||
title: "ID",
|
||||
key: "id",
|
||||
type: "number",
|
||||
column: {
|
||||
width: 50
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
name: {
|
||||
title: "用户姓名",
|
||||
search: { show: true },
|
||||
type: "text",
|
||||
column: {
|
||||
sortable: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<div>
|
||||
<div>年级id:{{ gradeId }},当前选中值:{{ modelValue }}</div>
|
||||
<div style="height: 400px">
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted, watch } from "vue";
|
||||
import createCrudOptions from "./crud";
|
||||
import { useExpose, useCrud } from "@fast-crud/fast-crud";
|
||||
export default defineComponent({
|
||||
name: "SubTable",
|
||||
props: {
|
||||
modelValue: {},
|
||||
gradeId: {} //年级id,接收其他参数
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
setup(props, ctx) {
|
||||
// crud组件的ref
|
||||
const crudRef = ref();
|
||||
// crud 配置的ref
|
||||
const crudBinding = ref();
|
||||
// 暴露的方法
|
||||
const { expose } = useExpose({ crudRef, crudBinding });
|
||||
// 你的crud配置
|
||||
const { crudOptions } = createCrudOptions({ expose, props, ctx });
|
||||
// 初始化crud配置
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-unused-vars
|
||||
const { resetCrudOptions } = useCrud({ expose, crudOptions });
|
||||
// 你可以调用此方法,重新初始化crud配置
|
||||
// resetCrudOptions(options)
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
expose.doRefresh();
|
||||
});
|
||||
|
||||
//你的业务代码
|
||||
watch(
|
||||
() => {
|
||||
return props.modelValue;
|
||||
},
|
||||
(value) => {
|
||||
console.log("modelValue changed", value);
|
||||
}
|
||||
);
|
||||
return {
|
||||
crudBinding,
|
||||
crudRef
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
/deep/.fs-crud-container.compact .el-table--border {
|
||||
border-left: 1px solid #eee;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,19 @@
|
||||
import mockUtil from "/src/mock/base";
|
||||
const options = {
|
||||
name: "AdvancedSubTable",
|
||||
idGenerator: 0
|
||||
};
|
||||
const list = [
|
||||
{
|
||||
name: "张三"
|
||||
},
|
||||
{
|
||||
name: "李四"
|
||||
},
|
||||
{
|
||||
name: "王五"
|
||||
}
|
||||
];
|
||||
options.list = list;
|
||||
const mock = mockUtil.buildMock(options);
|
||||
export default mock;
|
||||
@@ -0,0 +1,50 @@
|
||||
import { requestForMock } from "/src/api/service";
|
||||
const request = requestForMock;
|
||||
const apiPrefix = "/mock/BasisColumnsSet";
|
||||
export function GetList(query) {
|
||||
return request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "get",
|
||||
data: query
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj) {
|
||||
return request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj) {
|
||||
return request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetObj(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/info",
|
||||
method: "get",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function BatchDelete(ids) {
|
||||
return request({
|
||||
url: apiPrefix + "/batchDelete",
|
||||
method: "post",
|
||||
data: { ids }
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import * as api from "./api";
|
||||
import { dict } from "@fast-crud/fast-crud";
|
||||
import { ref } from "vue";
|
||||
export default function ({ expose }) {
|
||||
const pageRequest = async (query) => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }) => {
|
||||
form.id = row.id;
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({ row }) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
|
||||
const addRequest = async ({ form }) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
return {
|
||||
crudOptions: {
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
},
|
||||
toolbar: {
|
||||
columnsFilter: {
|
||||
mode: "default"
|
||||
}
|
||||
},
|
||||
columns: {
|
||||
id: {
|
||||
title: "ID",
|
||||
key: "id",
|
||||
type: "number",
|
||||
column: {
|
||||
width: 50
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
radio: {
|
||||
title: "状态",
|
||||
search: { show: true },
|
||||
type: "dict-radio",
|
||||
dict: dict({
|
||||
url: "/mock/dicts/OpenStatusEnum?single"
|
||||
})
|
||||
},
|
||||
disabled: {
|
||||
title: "列设置禁用",
|
||||
type: "text",
|
||||
column: {
|
||||
columnSetDisabled: true
|
||||
}
|
||||
},
|
||||
hidden: {
|
||||
title: "列设置隐藏",
|
||||
type: "text",
|
||||
column: {
|
||||
columnSetShow: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<fs-page>
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding">
|
||||
<template #actionbar-right>
|
||||
<a-alert class="ml-1" type="warning" message="列设置可以禁用或者隐藏某字段勾选" />
|
||||
<a-button @click="columnsSetToggleMode()"> 切换简单模式 </a-button>
|
||||
</template>
|
||||
</fs-crud>
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, onMounted, Ref } from "vue";
|
||||
import createCrudOptions from "./crud.jsx";
|
||||
import { useExpose, useCrud, CrudBinding } from "@fast-crud/fast-crud";
|
||||
import { message, Modal, notification } from "ant-design-vue";
|
||||
export default defineComponent({
|
||||
name: "BasisColumnsSet",
|
||||
setup() {
|
||||
// crud组件的ref
|
||||
const crudRef = ref();
|
||||
// crud 配置的ref
|
||||
const crudBinding: Ref<CrudBinding> = ref({});
|
||||
// 暴露的方法
|
||||
const { expose } = useExpose({ crudRef, crudBinding });
|
||||
// 你的crud配置
|
||||
const { crudOptions, selectedRowKeys } = createCrudOptions({ expose });
|
||||
// 初始化crud配置
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-unused-vars
|
||||
const { resetCrudOptions } = useCrud({ expose, crudOptions });
|
||||
// 你可以调用此方法,重新初始化crud配置
|
||||
// resetCrudOptions(options)
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
expose.doRefresh();
|
||||
});
|
||||
|
||||
function columnsSetToggleMode() {
|
||||
crudBinding.value.toolbar.columnsFilter.mode =
|
||||
crudBinding.value.toolbar.columnsFilter.mode === "simple" ? "default" : "simple";
|
||||
message.info("当前列设置组件的模式为:" + crudBinding.value.toolbar.columnsFilter.mode);
|
||||
}
|
||||
|
||||
return {
|
||||
crudBinding,
|
||||
crudRef,
|
||||
columnsSetToggleMode
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,19 @@
|
||||
import mockUtil from "/src/mock/base";
|
||||
const options = {
|
||||
name: "BasisColumnsSet",
|
||||
idGenerator: 0
|
||||
};
|
||||
const list = [
|
||||
{
|
||||
radio: "1"
|
||||
},
|
||||
{
|
||||
radio: "2"
|
||||
},
|
||||
{
|
||||
radio: "0"
|
||||
}
|
||||
];
|
||||
options.list = list;
|
||||
const mock = mockUtil.buildMock(options);
|
||||
export default mock;
|
||||
@@ -0,0 +1,42 @@
|
||||
import { requestForMock } from "/src/api/service";
|
||||
const request = requestForMock;
|
||||
const apiPrefix = "/mock/FormComputeMore";
|
||||
export function GetList(query) {
|
||||
return request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "get",
|
||||
data: query
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj) {
|
||||
return request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj) {
|
||||
return request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetObj(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/info",
|
||||
method: "get",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import * as api from "./api";
|
||||
import { requestForMock } from "/src/api/service";
|
||||
import { useCompute } from "@fast-crud/fast-crud";
|
||||
import { message } from "ant-design-vue";
|
||||
import { ref, computed } from "vue";
|
||||
const { asyncCompute, compute } = useCompute();
|
||||
export default function ({ expose }) {
|
||||
const pageRequest = async (query) => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }) => {
|
||||
form.id = row.id;
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({ row }) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
|
||||
const addRequest = async ({ form }) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
|
||||
//普通的ref引用,可以动态切换配置
|
||||
const defValueRef = ref("我是动态的默认值");
|
||||
const defValueComputed = computed(() => {
|
||||
return defValueRef.value;
|
||||
});
|
||||
return {
|
||||
output: {
|
||||
defValueRef,
|
||||
defValueComputed
|
||||
},
|
||||
crudOptions: {
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
},
|
||||
table: {
|
||||
scroll: {
|
||||
x: 1500
|
||||
}
|
||||
},
|
||||
form: {
|
||||
labelCol: { span: 8 },
|
||||
wrapperCol: { span: 14 }
|
||||
},
|
||||
rowHandle: {
|
||||
fixed: "right",
|
||||
align:'center',
|
||||
},
|
||||
columns: {
|
||||
id: {
|
||||
title: "ID",
|
||||
key: "id",
|
||||
type: "number",
|
||||
column: {
|
||||
width: 50
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
defValue: {
|
||||
title: "默认值",
|
||||
type: "text",
|
||||
search: { show: true, value: null },
|
||||
form: {
|
||||
// form.value不支持asyncCompute
|
||||
// 假如你的默认值异步获取的,那么你自己必须保证先异步计算完成之后,才能打开对话框。
|
||||
// 因为在打开对话框时,默认值就必须得设置好。
|
||||
value: defValueRef
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<fs-page>
|
||||
<template #header>
|
||||
<div class="title">动态计算-更多测试</div>
|
||||
</template>
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted } from "vue";
|
||||
import { useCrud, useExpose } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
export default defineComponent({
|
||||
name: "BasisComputeMore",
|
||||
setup() {
|
||||
// crud组件的ref
|
||||
const crudRef = ref();
|
||||
// crud 配置的ref
|
||||
const crudBinding = ref();
|
||||
// 暴露的方法
|
||||
const { expose } = useExpose({ crudRef, crudBinding });
|
||||
// 你的crud配置
|
||||
const { crudOptions, output } = createCrudOptions({ expose });
|
||||
// 初始化crud配置
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-unused-vars
|
||||
const { resetCrudOptions } = useCrud({ expose, crudOptions });
|
||||
// 你可以调用此方法,重新初始化crud配置
|
||||
// resetCrudOptions(options)
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
expose.doRefresh();
|
||||
});
|
||||
|
||||
return {
|
||||
crudBinding,
|
||||
crudRef,
|
||||
...output
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,32 @@
|
||||
import mockUtil from "/src/mock/base";
|
||||
const options = {
|
||||
name: "FormComputeMore",
|
||||
idGenerator: 0
|
||||
};
|
||||
const list = [
|
||||
{
|
||||
ref: "根据showRef显示",
|
||||
compute: true,
|
||||
status: "1",
|
||||
remote: "2",
|
||||
shower: "---> 点右边编辑查看示例效果",
|
||||
remote2: "2",
|
||||
editable: true
|
||||
},
|
||||
{
|
||||
compute: false,
|
||||
status: "2",
|
||||
remote: "0",
|
||||
remote2: "2",
|
||||
editable: false
|
||||
},
|
||||
{
|
||||
compute: true,
|
||||
status: "0",
|
||||
remote2: "2",
|
||||
editable: true
|
||||
}
|
||||
];
|
||||
options.list = list;
|
||||
const mock = mockUtil.buildMock(options);
|
||||
export default mock;
|
||||
@@ -0,0 +1,42 @@
|
||||
import { requestForMock } from "/src/api/service";
|
||||
const request = requestForMock;
|
||||
const apiPrefix = "/mock/FormCompute";
|
||||
export function GetList(query) {
|
||||
return request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "get",
|
||||
data: query
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj) {
|
||||
return request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj) {
|
||||
return request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetObj(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/info",
|
||||
method: "get",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
import * as api from "./api";
|
||||
import { requestForMock } from "/src/api/service";
|
||||
import { useCompute } from "@fast-crud/fast-crud";
|
||||
import { message } from "ant-design-vue";
|
||||
import { ref, computed } from "vue";
|
||||
const { asyncCompute, compute } = useCompute();
|
||||
export default function ({ expose }) {
|
||||
const pageRequest = async (query) => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }) => {
|
||||
form.id = row.id;
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({ row }) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
|
||||
const addRequest = async ({ form }) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
|
||||
//普通的ref引用,可以动态切换配置
|
||||
const showRef = ref(false);
|
||||
const showTableRef = ref(true);
|
||||
const showTableComputed = computed(() => {
|
||||
return showTableRef.value;
|
||||
});
|
||||
|
||||
const columnComponentShowRef = ref(true);
|
||||
const columnComponentShowComputed = computed(() => {
|
||||
return columnComponentShowRef.value;
|
||||
});
|
||||
|
||||
return {
|
||||
output: {
|
||||
showRef,
|
||||
showTableRef,
|
||||
columnComponentShowRef
|
||||
},
|
||||
crudOptions: {
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
},
|
||||
table: {
|
||||
scroll: {
|
||||
x: 1500
|
||||
},
|
||||
//通过switch动态显隐table
|
||||
show: showTableComputed //不仅支持computed,直接传showTableRef也是可以的
|
||||
},
|
||||
form: {
|
||||
labelCol: { span: 8 },
|
||||
wrapperCol: { span: 14 }
|
||||
},
|
||||
rowHandle: {
|
||||
fixed: "right",
|
||||
buttons: {
|
||||
edit: {
|
||||
show: compute(({ row }) => {
|
||||
return row.editable;
|
||||
})
|
||||
},
|
||||
remove: {
|
||||
show: compute(({ row }) => {
|
||||
return row.editable;
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
columns: {
|
||||
id: {
|
||||
title: "ID",
|
||||
key: "id",
|
||||
type: "number",
|
||||
column: {
|
||||
width: 50,
|
||||
resizable: true
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
refSwitch: {
|
||||
title: "ref引用切换",
|
||||
type: "text",
|
||||
form: {
|
||||
helper: "点我切换右边的输入框显示"
|
||||
}
|
||||
},
|
||||
ref: {
|
||||
title: "根据ref引用显示",
|
||||
type: ["text"],
|
||||
form: {
|
||||
component: {
|
||||
show: showRef
|
||||
},
|
||||
helper: "我会根据showRef进行显隐"
|
||||
}
|
||||
},
|
||||
compute: {
|
||||
title: "compute",
|
||||
search: { show: false },
|
||||
type: "text",
|
||||
column: {
|
||||
show: columnComponentShowComputed,
|
||||
columnSetDisabled: true, //这里采用自定义控制显隐,那么列设置里面就要禁用
|
||||
// columnSetShow: false, //直接不在列设置里面显示也行
|
||||
component: {
|
||||
name: "a-switch",
|
||||
vModel: "checked"
|
||||
}
|
||||
},
|
||||
form: {
|
||||
component: {
|
||||
name: "a-switch",
|
||||
vModel: "checked"
|
||||
},
|
||||
helper: "点我触发动态计算"
|
||||
}
|
||||
},
|
||||
shower: {
|
||||
title: "根据compute显示",
|
||||
search: { show: false },
|
||||
type: "button",
|
||||
form: {
|
||||
component: {
|
||||
show: compute(({ form }) => {
|
||||
return form.compute;
|
||||
})
|
||||
}
|
||||
},
|
||||
column: {
|
||||
width: 250,
|
||||
resizable: true,
|
||||
component: {
|
||||
show: compute(({ row }) => {
|
||||
return row.compute;
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
remote: {
|
||||
title: "asyncCompute",
|
||||
search: { show: true },
|
||||
type: "text",
|
||||
form: {
|
||||
component: {
|
||||
name: "a-select",
|
||||
vModel: "value",
|
||||
placeholder: "异步计算远程获取options",
|
||||
options: asyncCompute({
|
||||
async asyncFn(watchValue, context) {
|
||||
const url = "/mock/dicts/OpenStatusEnum?remote";
|
||||
return await requestForMock({ url });
|
||||
}
|
||||
})
|
||||
},
|
||||
helper: "我的options是异步计算远程获取的,只会获取一次"
|
||||
}
|
||||
},
|
||||
remote2: {
|
||||
title: "监听switch触发异步计算",
|
||||
search: { show: false },
|
||||
type: "text",
|
||||
form: {
|
||||
component: {
|
||||
name: "a-select",
|
||||
vModel: "value",
|
||||
placeholder: "异步计算远程获取options",
|
||||
options: asyncCompute({
|
||||
watch({ form }) {
|
||||
return form.compute;
|
||||
},
|
||||
async asyncFn(watchValue) {
|
||||
message.info("监听switch,触发远程获取options");
|
||||
const url = watchValue
|
||||
? "/mock/dicts/OpenStatusEnum?remote"
|
||||
: "/mock/dicts/moreOpenStatusEnum?remote";
|
||||
return await requestForMock({ url });
|
||||
}
|
||||
})
|
||||
},
|
||||
helper: "监听其他属性修改后,触发重新计算"
|
||||
},
|
||||
column: {
|
||||
width: 200
|
||||
}
|
||||
},
|
||||
editable: {
|
||||
title: "可编辑",
|
||||
search: { show: false },
|
||||
type: "text",
|
||||
column: {
|
||||
fixed: "right",
|
||||
component: {
|
||||
name: "a-switch",
|
||||
vModel: "checked"
|
||||
}
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<fs-page>
|
||||
<template #header>
|
||||
<div class="title">
|
||||
动态计算
|
||||
<fs-icon icon="ion:apps-sharp" :spin="true" />
|
||||
</div>
|
||||
<div class="more">
|
||||
<a target="_blank" href="http://fast-crud.docmirror.cn/guide/advance/compute.html">帮助说明</a>
|
||||
</div>
|
||||
</template>
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding">
|
||||
<template #actionbar-right>
|
||||
<a-tooltip title="我能控制表格显隐">
|
||||
<span class="ml-1">表格显隐:<a-switch v-model:checked="showTableRef"></a-switch></span>
|
||||
</a-tooltip>
|
||||
<span class="ml-1">列显隐:<a-switch v-model:checked="columnComponentShowRef"></a-switch></span>
|
||||
<a-alert class="ml-1" type="info" message="点击下方右边的编辑按钮查看示例效果-----------> ↓↓↓↓↓" />
|
||||
</template>
|
||||
<template #form_refSwitch>
|
||||
<a-switch v-model:checked="showRef"></a-switch>
|
||||
</template>
|
||||
</fs-crud>
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted } from "vue";
|
||||
import { useCrud, useExpose } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
export default defineComponent({
|
||||
name: "BasisCompute",
|
||||
setup() {
|
||||
// crud组件的ref
|
||||
const crudRef = ref();
|
||||
// crud 配置的ref
|
||||
const crudBinding = ref();
|
||||
// 暴露的方法
|
||||
const { expose } = useExpose({ crudRef, crudBinding });
|
||||
// 你的crud配置
|
||||
const { crudOptions, output } = createCrudOptions({ expose });
|
||||
// 初始化crud配置
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-unused-vars
|
||||
const { resetCrudOptions } = useCrud({ expose, crudOptions });
|
||||
// 你可以调用此方法,重新初始化crud配置
|
||||
// resetCrudOptions(options)
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
expose.doRefresh();
|
||||
});
|
||||
|
||||
return {
|
||||
crudBinding,
|
||||
crudRef,
|
||||
...output
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,34 @@
|
||||
import mockUtil from "/src/mock/base";
|
||||
const options = {
|
||||
name: "FormCompute",
|
||||
idGenerator: 0
|
||||
};
|
||||
const list = [
|
||||
{
|
||||
ref: "根据showRef显示",
|
||||
compute: true,
|
||||
status: "1",
|
||||
remote: "2",
|
||||
shower: "---> 点右边编辑查看示例效果",
|
||||
remote2: "2",
|
||||
editable: true
|
||||
},
|
||||
{
|
||||
compute: false,
|
||||
shower: "---> 点右边编辑查看示例效果",
|
||||
status: "2",
|
||||
remote: "0",
|
||||
remote2: "2",
|
||||
editable: false
|
||||
},
|
||||
{
|
||||
compute: true,
|
||||
shower: "---> 点右边编辑查看示例效果",
|
||||
status: "0",
|
||||
remote2: "2",
|
||||
editable: true
|
||||
}
|
||||
];
|
||||
options.list = list;
|
||||
const mock = mockUtil.buildMock(options);
|
||||
export default mock;
|
||||
@@ -0,0 +1,109 @@
|
||||
<template>
|
||||
<fs-page>
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding" />
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted } from "vue";
|
||||
import { dict, useCrud } from "@fast-crud/fast-crud";
|
||||
import { useExpose } from "@fast-crud/fast-crud";
|
||||
import _ from "lodash-es";
|
||||
|
||||
//此处为crudOptions配置
|
||||
const createCrudOptions = function ({ expose }) {
|
||||
//本地模拟后台crud接口方法 ----开始
|
||||
const records = [{ id: 1, name: "Hello World", type: 1 }];
|
||||
const pageRequest = async (query) => {
|
||||
return {
|
||||
records,
|
||||
currentPage: 1,
|
||||
pageSize: 20,
|
||||
total: records.length
|
||||
};
|
||||
};
|
||||
const editRequest = async ({ form, row }) => {
|
||||
const target = _.find(records, (item) => {
|
||||
return row.id === item.id;
|
||||
});
|
||||
_.merge(target, form);
|
||||
return target;
|
||||
};
|
||||
const delRequest = async ({ row }) => {
|
||||
_.remove(records, (item) => {
|
||||
return item.id === row.id;
|
||||
});
|
||||
};
|
||||
const addRequest = async ({ form }) => {
|
||||
const maxRecord = _.maxBy(records, (item) => {
|
||||
return item.id;
|
||||
});
|
||||
form.id = (maxRecord?.id || 0) + 1;
|
||||
records.push(form);
|
||||
return form;
|
||||
};
|
||||
//本地模拟后台crud接口方法 -----结束
|
||||
|
||||
return {
|
||||
crudOptions: {
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
},
|
||||
columns: {
|
||||
name: {
|
||||
title: "姓名",
|
||||
type: "text",
|
||||
search: { show: true },
|
||||
column: {
|
||||
resizable: true,
|
||||
width: 200
|
||||
}
|
||||
},
|
||||
type: {
|
||||
title: "类型",
|
||||
type: "dict-select",
|
||||
dict: dict({
|
||||
data: [
|
||||
{ value: 1, label: "开始" },
|
||||
{ value: 0, label: "停止" }
|
||||
]
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
//此处为组件定义
|
||||
export default defineComponent({
|
||||
name: "FsCrudFirst",
|
||||
setup() {
|
||||
// crud组件的ref
|
||||
const crudRef = ref();
|
||||
// crud 配置的ref
|
||||
const crudBinding = ref();
|
||||
// 暴露的方法
|
||||
const { expose } = useExpose({ crudRef, crudBinding });
|
||||
// 你的crud配置
|
||||
const { crudOptions } = createCrudOptions({ expose });
|
||||
// 初始化crud配置
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-unused-vars
|
||||
const { resetCrudOptions } = useCrud({ expose, crudOptions });
|
||||
// 你可以调用此方法,重新初始化crud配置
|
||||
// resetCrudOptions(options)
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
expose.doRefresh();
|
||||
});
|
||||
|
||||
return {
|
||||
crudBinding,
|
||||
crudRef
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,42 @@
|
||||
import { requestForMock } from "/src/api/service";
|
||||
const request = requestForMock;
|
||||
const apiPrefix = "/mock/BasisI18n";
|
||||
export function GetList(query) {
|
||||
return request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "get",
|
||||
data: query
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj) {
|
||||
return request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj) {
|
||||
return request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetObj(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/info",
|
||||
method: "get",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import * as api from "./api";
|
||||
import { dict } from "@fast-crud/fast-crud";
|
||||
import { useI18n } from "vue-i18n";
|
||||
export default function ({ expose }) {
|
||||
const { t } = useI18n();
|
||||
const pageRequest = async (query) => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }) => {
|
||||
form.id = row.id;
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({ row }) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
|
||||
const addRequest = async ({ form }) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
return {
|
||||
crudOptions: {
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
},
|
||||
columns: {
|
||||
id: {
|
||||
title: "ID",
|
||||
key: "id",
|
||||
type: "number",
|
||||
column: {
|
||||
width: 50
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
name: {
|
||||
title: t("app.crud.i18n.name"),
|
||||
type: "text",
|
||||
search: { show: true }
|
||||
},
|
||||
city: {
|
||||
title: t("app.crud.i18n.city"),
|
||||
type: "dict-select",
|
||||
search: { show: true },
|
||||
dict: dict({
|
||||
value: "id",
|
||||
label: "text",
|
||||
data: [
|
||||
{ id: "sz", text: "深圳", color: "success" },
|
||||
{ id: "gz", text: "广州", color: "blue" },
|
||||
{ id: "bj", text: "北京" },
|
||||
{ id: "wh", text: "武汉" },
|
||||
{ id: "sh", text: "上海" }
|
||||
]
|
||||
})
|
||||
},
|
||||
radio: {
|
||||
title: t("app.crud.i18n.status"),
|
||||
search: { show: true },
|
||||
type: "dict-radio",
|
||||
dict: dict({
|
||||
url: "/mock/dicts/OpenStatusEnum?single"
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<fs-page>
|
||||
<template #header>
|
||||
<div class="title">CRUD示例【国际化】</div>
|
||||
<div class="more"><a-button @click="showDemo">更多</a-button></div>
|
||||
</template>
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding">
|
||||
<template #actionbar-right>
|
||||
<a-alert class="ml-1" type="warning" message="右上角切换语言查看效果" />
|
||||
</template>
|
||||
</fs-crud>
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted } from "vue";
|
||||
import { useCrud } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
import { useExpose } from "@fast-crud/fast-crud";
|
||||
import { message } from "ant-design-vue";
|
||||
export default defineComponent({
|
||||
name: "BasisI18n",
|
||||
setup() {
|
||||
// crud组件的ref
|
||||
const crudRef = ref();
|
||||
// crud 配置的ref
|
||||
const crudBinding = ref();
|
||||
// 暴露的方法
|
||||
const { expose } = useExpose({ crudRef, crudBinding });
|
||||
// 你的crud配置
|
||||
const { crudOptions } = createCrudOptions({ expose });
|
||||
// 初始化crud配置
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-unused-vars
|
||||
const { resetCrudOptions } = useCrud({ expose, crudOptions });
|
||||
// 你可以调用此方法,重新初始化crud配置
|
||||
// resetCrudOptions(options)
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
expose.doRefresh();
|
||||
});
|
||||
|
||||
function showDemo() {
|
||||
message("演示按钮");
|
||||
}
|
||||
return {
|
||||
crudBinding,
|
||||
crudRef,
|
||||
showDemo
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,25 @@
|
||||
import mockUtil from "/src/mock/base";
|
||||
const options = {
|
||||
name: "BasisI18n",
|
||||
idGenerator: 0
|
||||
};
|
||||
const list = [
|
||||
{
|
||||
radio: "1",
|
||||
name: "张三",
|
||||
city: "sz"
|
||||
},
|
||||
{
|
||||
radio: "2",
|
||||
name: "李四",
|
||||
city: "gz"
|
||||
},
|
||||
{
|
||||
radio: "0",
|
||||
name: "王五",
|
||||
city: "sh"
|
||||
}
|
||||
];
|
||||
options.list = list;
|
||||
const mock = mockUtil.buildMock(options);
|
||||
export default mock;
|
||||
@@ -0,0 +1,42 @@
|
||||
import { requestForMock } from "/src/api/service";
|
||||
const request = requestForMock;
|
||||
const apiPrefix = "/mock/BasisLayoutCard";
|
||||
export function GetList(query) {
|
||||
return request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "get",
|
||||
data: query
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj) {
|
||||
return request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj) {
|
||||
return request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetObj(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/info",
|
||||
method: "get",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import * as api from "./api";
|
||||
import { dict } from "@fast-crud/fast-crud";
|
||||
export default function ({ crudExpose }) {
|
||||
const pageRequest = async (query) => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }) => {
|
||||
form.id = row.id;
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({ row }) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
|
||||
const addRequest = async ({ form }) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
return {
|
||||
crudOptions: {
|
||||
container: {
|
||||
is: "fs-layout-card"
|
||||
},
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
},
|
||||
columns: {
|
||||
id: {
|
||||
title: "ID",
|
||||
key: "id",
|
||||
type: "number",
|
||||
column: {
|
||||
width: 50
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
name: {
|
||||
title: "姓名",
|
||||
type: "text",
|
||||
search: { show: true }
|
||||
},
|
||||
city: {
|
||||
title: "城市",
|
||||
type: "dict-select",
|
||||
search: { show: true },
|
||||
dict: dict({
|
||||
value: "id",
|
||||
label: "text",
|
||||
data: [
|
||||
{ id: "sz", text: "深圳", color: "success" },
|
||||
{ id: "gz", text: "广州", color: "blue" },
|
||||
{ id: "bj", text: "北京" },
|
||||
{ id: "wh", text: "武汉" },
|
||||
{ id: "sh", text: "上海" }
|
||||
]
|
||||
})
|
||||
},
|
||||
radio: {
|
||||
title: "单选",
|
||||
search: { show: true },
|
||||
type: "dict-radio",
|
||||
dict: dict({
|
||||
url: "/mock/dicts/OpenStatusEnum?single"
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<fs-page class="page-layout-card">
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted } from "vue";
|
||||
import { useCrud } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
import { useExpose } from "@fast-crud/fast-crud";
|
||||
export default defineComponent({
|
||||
name: "BasisLayoutCard",
|
||||
setup() {
|
||||
// crud组件的ref
|
||||
const crudRef = ref();
|
||||
// crud 配置的ref
|
||||
const crudBinding = ref();
|
||||
// 暴露的方法
|
||||
const { crudExpose } = useExpose({ crudRef, crudBinding });
|
||||
// 你的crud配置
|
||||
const { crudOptions } = createCrudOptions({ crudExpose });
|
||||
// 初始化crud配置
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-unused-vars
|
||||
const { resetCrudOptions } = useCrud({ crudExpose, crudOptions });
|
||||
// 你可以调用此方法,重新初始化crud配置
|
||||
// resetCrudOptions(options)
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
|
||||
return {
|
||||
crudBinding,
|
||||
crudRef
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.page-layout-card {
|
||||
background-color: #eee;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,25 @@
|
||||
import mockUtil from "/src/mock/base";
|
||||
const options = {
|
||||
name: "BasisLayoutCard",
|
||||
idGenerator: 0
|
||||
};
|
||||
const list = [
|
||||
{
|
||||
radio: "1",
|
||||
name: "张三",
|
||||
city: "sz"
|
||||
},
|
||||
{
|
||||
radio: "2",
|
||||
name: "李四",
|
||||
city: "gz"
|
||||
},
|
||||
{
|
||||
radio: "0",
|
||||
name: "王五",
|
||||
city: "sh"
|
||||
}
|
||||
];
|
||||
options.list = list;
|
||||
const mock = mockUtil.buildMock(options);
|
||||
export default mock;
|
||||
@@ -0,0 +1,42 @@
|
||||
import { requestForMock } from "/src/api/service";
|
||||
const request = requestForMock;
|
||||
const apiPrefix = "/mock/BasisLayoutCustom";
|
||||
export function GetList(query) {
|
||||
return request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "get",
|
||||
data: query
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj) {
|
||||
return request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj) {
|
||||
return request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetObj(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/info",
|
||||
method: "get",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
import * as api from "./api";
|
||||
import { dict } from "@fast-crud/fast-crud";
|
||||
import CustomLayout from "./custom-layout.vue";
|
||||
import { shallowRef } from "vue";
|
||||
export default function ({ crudExpose }) {
|
||||
const pageRequest = async (query) => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }) => {
|
||||
form.id = row.id;
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({ row }) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
|
||||
const addRequest = async ({ form }) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
return {
|
||||
crudOptions: {
|
||||
container: {
|
||||
is: shallowRef(CustomLayout) //可以将自定义布局组件全局注册,这里只需要配置name即可
|
||||
},
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
},
|
||||
columns: {
|
||||
id: {
|
||||
title: "ID",
|
||||
key: "id",
|
||||
type: "number",
|
||||
column: {
|
||||
width: 50
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
name: {
|
||||
title: "姓名",
|
||||
type: "text",
|
||||
search: { show: true }
|
||||
},
|
||||
city: {
|
||||
title: "城市",
|
||||
type: "dict-select",
|
||||
search: { show: false },
|
||||
dict: dict({
|
||||
value: "id",
|
||||
label: "text",
|
||||
data: [
|
||||
{ id: "sz", text: "深圳", color: "success" },
|
||||
{ id: "gz", text: "广州", color: "blue" },
|
||||
{ id: "bj", text: "北京" },
|
||||
{ id: "wh", text: "武汉" },
|
||||
{ id: "sh", text: "上海" }
|
||||
]
|
||||
})
|
||||
},
|
||||
radio: {
|
||||
title: "单选",
|
||||
search: { show: false },
|
||||
type: "dict-radio",
|
||||
dict: dict({
|
||||
url: "/mock/dicts/OpenStatusEnum?single"
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<div class="custom-layout">
|
||||
<div class="layout-header">
|
||||
<!-- ↓↓↓↓↓ 关键插槽:查询 ↓↓↓↓ -->
|
||||
<slot name="search"></slot>
|
||||
<!-- ↓↓↓↓↓ 关键插槽:工具条 ↓↓↓↓ -->
|
||||
<slot name="toolbar"></slot>
|
||||
</div>
|
||||
<div class="layout-top">
|
||||
<!-- ↓↓↓↓↓ 关键插槽:动作条 ↓↓↓↓ -->
|
||||
<slot name="actionbar"></slot>
|
||||
<!-- ↓↓↓↓↓ 上翻页条 ↓↓↓↓ -->
|
||||
<slot name="pagination"></slot>
|
||||
</div>
|
||||
|
||||
<!-- 高度需要自适应撑开,可以通过flex:1 -->
|
||||
<div class="layout-body">
|
||||
<!-- 默认插槽 -->
|
||||
<slot></slot>
|
||||
<!-- ↓↓↓↓↓ 关键插槽:表格 ↓↓↓↓ -->
|
||||
<slot name="table"></slot>
|
||||
|
||||
<!-- ↓↓↓↓↓ 关键插槽:表单 ↓↓↓↓ -->
|
||||
<slot name="form"></slot>
|
||||
</div>
|
||||
<div class="layout-footer">
|
||||
<!-- ↓↓↓↓↓ 关键插槽:分页条 ↓↓↓↓ -->
|
||||
<slot name="pagination"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
/**
|
||||
* 自定义布局
|
||||
*/
|
||||
export default defineComponent({
|
||||
name: "CustomLayout"
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.custom-layout {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.layout-header {
|
||||
padding: 10px 10px 5px 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.layout-top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 5px 10px 5px 10px;
|
||||
}
|
||||
.layout-body {
|
||||
flex: 1; //重要,自适应撑开高度,表格固定表头必须
|
||||
overflow-y: auto;
|
||||
}
|
||||
.fs-crud-actionbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.fs-crud-pagination {
|
||||
text-align: right;
|
||||
padding: 5px 10px 5px 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<fs-page class="page-layout-custom">
|
||||
<template #header>
|
||||
<div class="title">自定义布局</div>
|
||||
</template>
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding">
|
||||
<template #actionbar-right>
|
||||
<a-alert
|
||||
class="ml-1"
|
||||
type="info"
|
||||
message="通过自定义container.is可以自定义布局,甚至可以支持上下两个翻页条 -------->"
|
||||
/>
|
||||
</template>
|
||||
</fs-crud>
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted } from "vue";
|
||||
import { useCrud } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
import { useExpose } from "@fast-crud/fast-crud";
|
||||
export default defineComponent({
|
||||
name: "BasisLayoutCustom",
|
||||
setup() {
|
||||
// crud组件的ref
|
||||
const crudRef = ref();
|
||||
// crud 配置的ref
|
||||
const crudBinding = ref();
|
||||
// 暴露的方法
|
||||
const { crudExpose } = useExpose({ crudRef, crudBinding });
|
||||
// 你的crud配置
|
||||
const { crudOptions } = createCrudOptions({ crudExpose });
|
||||
// 初始化crud配置
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-unused-vars
|
||||
const { resetCrudOptions } = useCrud({ crudExpose, crudOptions });
|
||||
// 你可以调用此方法,重新初始化crud配置
|
||||
// resetCrudOptions(options)
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
|
||||
return {
|
||||
crudBinding,
|
||||
crudRef
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.page-layout-custom {
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,25 @@
|
||||
import mockUtil from "/src/mock/base";
|
||||
const options = {
|
||||
name: "BasisLayoutCustom",
|
||||
idGenerator: 0
|
||||
};
|
||||
const list = [
|
||||
{
|
||||
radio: "1",
|
||||
name: "张三",
|
||||
city: "sz"
|
||||
},
|
||||
{
|
||||
radio: "2",
|
||||
name: "李四",
|
||||
city: "gz"
|
||||
},
|
||||
{
|
||||
radio: "0",
|
||||
name: "王五",
|
||||
city: "sh"
|
||||
}
|
||||
];
|
||||
options.list = list;
|
||||
const mock = mockUtil.buildMock(options);
|
||||
export default mock;
|
||||
@@ -0,0 +1,42 @@
|
||||
import { requestForMock } from "/src/api/service";
|
||||
const request = requestForMock;
|
||||
const apiPrefix = "/mock/BasisValueChange";
|
||||
export function GetList(query) {
|
||||
return request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "get",
|
||||
data: query
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj) {
|
||||
return request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj) {
|
||||
return request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetObj(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/info",
|
||||
method: "get",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
import * as api from "./api";
|
||||
import { message } from "ant-design-vue";
|
||||
import { dict } from "@fast-crud/fast-crud";
|
||||
export default function ({ expose }) {
|
||||
const pageRequest = async (query) => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }) => {
|
||||
form.id = row.id;
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({ row }) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
|
||||
const addRequest = async ({ form }) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
|
||||
return {
|
||||
output: {},
|
||||
crudOptions: {
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
},
|
||||
columns: {
|
||||
id: {
|
||||
title: "ID",
|
||||
key: "id",
|
||||
type: "number",
|
||||
column: {
|
||||
width: 50
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
switch: {
|
||||
title: "开关",
|
||||
type: "dict-switch",
|
||||
dict: dict({
|
||||
data: [
|
||||
{ value: true, label: "开启" },
|
||||
{ value: false, label: "关闭" }
|
||||
]
|
||||
}),
|
||||
column: {
|
||||
component: {
|
||||
name: "fs-dict-switch",
|
||||
vModel: "checked"
|
||||
},
|
||||
valueChange(context) {
|
||||
console.log("column value changed:", context);
|
||||
}
|
||||
},
|
||||
form: {
|
||||
valueChange({ value, key, form }) {
|
||||
console.log("valueChanged,", key, value, form);
|
||||
message.info(`valueChanged:${key}=${value}`);
|
||||
}
|
||||
}
|
||||
},
|
||||
normal: {
|
||||
title: "value-change",
|
||||
type: "text",
|
||||
form: {
|
||||
valueChange({ value, key, form }) {
|
||||
console.log("valueChanged,", key, value, form);
|
||||
message.info(`valueChanged:${key}=${value}`);
|
||||
}
|
||||
}
|
||||
},
|
||||
immediate: {
|
||||
title: "immediate",
|
||||
type: "text",
|
||||
search: {
|
||||
show: true
|
||||
},
|
||||
form: {
|
||||
valueChange: {
|
||||
handle({ value, key, form, immediate }) {
|
||||
console.log("valueChange,", key, value, "isImmediate=", immediate);
|
||||
message.info(`valueChanged:${key}=${value},isImmediate=${immediate}`);
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<fs-page>
|
||||
<template #header>
|
||||
<div class="title">ValueChange</div>
|
||||
</template>
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted } from "vue";
|
||||
import { useCrud, useExpose } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
export default defineComponent({
|
||||
name: "BasisValueChange",
|
||||
setup() {
|
||||
// crud组件的ref
|
||||
const crudRef = ref();
|
||||
// crud 配置的ref
|
||||
const crudBinding = ref();
|
||||
// 暴露的方法
|
||||
const { expose } = useExpose({ crudRef, crudBinding });
|
||||
// 你的crud配置
|
||||
const { crudOptions, output } = createCrudOptions({ expose });
|
||||
// 初始化crud配置
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-unused-vars
|
||||
const { resetCrudOptions } = useCrud({ expose, crudOptions });
|
||||
// 你可以调用此方法,重新初始化crud配置
|
||||
// resetCrudOptions(options)
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
expose.doRefresh();
|
||||
});
|
||||
|
||||
return {
|
||||
crudBinding,
|
||||
crudRef,
|
||||
...output
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,32 @@
|
||||
import mockUtil from "/src/mock/base";
|
||||
const options = {
|
||||
name: "BasisValueChange",
|
||||
idGenerator: 0
|
||||
};
|
||||
const list = [
|
||||
{
|
||||
ref: "根据showRef显示",
|
||||
compute: true,
|
||||
status: "1",
|
||||
remote: "2",
|
||||
shower: "---> 点右边编辑查看示例效果",
|
||||
remote2: "2",
|
||||
editable: true
|
||||
},
|
||||
{
|
||||
compute: false,
|
||||
status: "2",
|
||||
remote: "0",
|
||||
remote2: "2",
|
||||
editable: false
|
||||
},
|
||||
{
|
||||
compute: true,
|
||||
status: "0",
|
||||
remote2: "2",
|
||||
editable: true
|
||||
}
|
||||
];
|
||||
options.list = list;
|
||||
const mock = mockUtil.buildMock(options);
|
||||
export default mock;
|
||||
@@ -0,0 +1,42 @@
|
||||
import { requestForMock } from "/src/api/service";
|
||||
const request = requestForMock;
|
||||
const apiPrefix = "/mock/ComponentButton";
|
||||
export function GetList(query) {
|
||||
return request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "get",
|
||||
data: query
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj) {
|
||||
return request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj) {
|
||||
return request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetObj(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/info",
|
||||
method: "get",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
import * as api from "./api";
|
||||
import { requestForMock } from "/src/api/service";
|
||||
import { dict, compute } from "@fast-crud/fast-crud";
|
||||
import { message } from "ant-design-vue";
|
||||
export default function ({ expose }) {
|
||||
const pageRequest = async (query) => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }) => {
|
||||
form.id = row.id;
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({ row }) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
|
||||
const addRequest = async ({ form }) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
return {
|
||||
crudOptions: {
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
},
|
||||
form: {
|
||||
//配置表单label的宽度
|
||||
labelCol: { span: 6 }
|
||||
},
|
||||
columns: {
|
||||
id: {
|
||||
title: "ID",
|
||||
key: "id",
|
||||
type: "number",
|
||||
column: {
|
||||
width: 50
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
button: {
|
||||
title: "按钮",
|
||||
search: { show: true },
|
||||
type: "button",
|
||||
column: {
|
||||
component: {
|
||||
show: compute(({ value }) => {
|
||||
//当value为null时,不显示
|
||||
return value != null;
|
||||
}),
|
||||
on: {
|
||||
// 注意:必须要on前缀
|
||||
onClick({ row }) {
|
||||
message.success("按钮点击:" + row.button);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
url: {
|
||||
title: "url",
|
||||
search: { show: true },
|
||||
type: "text",
|
||||
column: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
link: {
|
||||
title: "链接",
|
||||
search: { show: true },
|
||||
type: "link",
|
||||
column: {
|
||||
component: {
|
||||
on: {
|
||||
// 注意:必须要on前缀
|
||||
onClick({ row }) {
|
||||
if (row.url) {
|
||||
window.open(row.url);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
form: {
|
||||
title: "按钮文字"
|
||||
}
|
||||
},
|
||||
link2: {
|
||||
title: "手写link配置",
|
||||
search: { show: true },
|
||||
type: "text", //form组件用input
|
||||
column: {
|
||||
component: {
|
||||
name: "fs-button", //列展示组件为button
|
||||
vModel: "text", // 将row.link2的值赋值给text属性
|
||||
type: "link", // 按钮展示为链接样式
|
||||
on: {
|
||||
//注册点击事件
|
||||
// 注意:必须要on前缀
|
||||
onClick({ row }) {
|
||||
if (row.url) {
|
||||
window.open(row.url);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<fs-page>
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding" />
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted } from "vue";
|
||||
import { useCrud } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
import { useExpose } from "@fast-crud/fast-crud";
|
||||
export default defineComponent({
|
||||
name: "ComponentButton",
|
||||
setup() {
|
||||
// crud组件的ref
|
||||
const crudRef = ref();
|
||||
// crud 配置的ref
|
||||
const crudBinding = ref();
|
||||
// 暴露的方法
|
||||
const { expose } = useExpose({ crudRef, crudBinding });
|
||||
// 你的crud配置
|
||||
const { crudOptions } = createCrudOptions({ expose });
|
||||
// 初始化crud配置
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-unused-vars
|
||||
const { resetCrudOptions } = useCrud({ expose, crudOptions });
|
||||
// 你可以调用此方法,重新初始化crud配置
|
||||
// resetCrudOptions(options)
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
expose.doRefresh();
|
||||
});
|
||||
|
||||
return {
|
||||
crudBinding,
|
||||
crudRef
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less"></style>
|
||||
@@ -0,0 +1,23 @@
|
||||
import mockUtil from "/src/mock/base";
|
||||
const options = {
|
||||
name: "ComponentButton",
|
||||
idGenerator: 0
|
||||
};
|
||||
const list = [
|
||||
{
|
||||
button: "张三",
|
||||
link: "百度",
|
||||
url: "https://www.baidu.com",
|
||||
link2: "手写配置"
|
||||
},
|
||||
{
|
||||
button: "李四",
|
||||
link: "百度",
|
||||
url: "https://www.baidu.com",
|
||||
link2: "手写配置"
|
||||
},
|
||||
{}
|
||||
];
|
||||
options.list = list;
|
||||
const mock = mockUtil.buildMock(options);
|
||||
export default mock;
|
||||
@@ -0,0 +1,42 @@
|
||||
import { requestForMock } from "/src/api/service";
|
||||
const request = requestForMock;
|
||||
const apiPrefix = "/mock/ComponentCascader";
|
||||
export function GetList(query) {
|
||||
return request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "get",
|
||||
data: query
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj) {
|
||||
return request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj) {
|
||||
return request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetObj(id) {
|
||||
return request({
|
||||
url: apiPrefix + "/info",
|
||||
method: "get",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
import * as api from "./api";
|
||||
import { requestForMock } from "/src/api/service";
|
||||
import { dict } from "@fast-crud/fast-crud";
|
||||
export default function ({ crudRef }) {
|
||||
const pageRequest = async (query) => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }) => {
|
||||
form.id = row.id;
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({ row }) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
|
||||
const addRequest = async ({ form }) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
return {
|
||||
crudOptions: {
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
},
|
||||
form: {
|
||||
// 单列布局
|
||||
col: { span: 24 },
|
||||
labelCol: { span: 4 },
|
||||
wrapperCol: { span: 18 }
|
||||
},
|
||||
columns: {
|
||||
id: {
|
||||
title: "ID",
|
||||
key: "id",
|
||||
type: "number",
|
||||
column: {
|
||||
width: 50
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
cascader: {
|
||||
title: "级联",
|
||||
search: { show: true },
|
||||
type: "dict-cascader",
|
||||
dict: dict({
|
||||
cloneable: false,
|
||||
isTree: true,
|
||||
url: "/mock/dicts/cascaderData?single"
|
||||
})
|
||||
},
|
||||
lazyLoad: {
|
||||
title: "懒加载",
|
||||
type: "dict-cascader",
|
||||
dict: dict({
|
||||
url: "/mock/tree/GetTreeChildrenByParentId?lazyLoad",
|
||||
value: "code",
|
||||
label: "name",
|
||||
isTree: true,
|
||||
prototype: true,
|
||||
getNodesByValues(values) {
|
||||
//给cell展示组件调用,根据value值获取节点,每行都会请求一次
|
||||
if (values == null) {
|
||||
return [];
|
||||
}
|
||||
return requestForMock({
|
||||
url: "/mock/tree/GetNodesByValues",
|
||||
params: { values }
|
||||
});
|
||||
}
|
||||
}),
|
||||
form: {
|
||||
component: {
|
||||
vModel: "value",
|
||||
options: [
|
||||
{
|
||||
code: "11",
|
||||
name: "北京",
|
||||
isLeaf: false
|
||||
},
|
||||
{
|
||||
code: "12",
|
||||
name: "天津",
|
||||
isLeaf: false
|
||||
}
|
||||
],
|
||||
loadData: async (selectedOptions) => {
|
||||
console.log("lazyLoad", selectedOptions);
|
||||
const targetOption = selectedOptions[selectedOptions.length - 1];
|
||||
targetOption.loading = true;
|
||||
|
||||
const ret = await requestForMock({
|
||||
url: "/mock/tree/GetTreeChildrenByParentId",
|
||||
params: { parentId: targetOption.code }
|
||||
});
|
||||
targetOption.loading = false;
|
||||
const list = [];
|
||||
for (const item of ret) {
|
||||
list.push({
|
||||
code: item.code,
|
||||
name: item.name,
|
||||
isLeaf: item.leaf === true
|
||||
});
|
||||
}
|
||||
targetOption.children = list;
|
||||
//options.value = [...options.value];
|
||||
},
|
||||
changeOnSelect: true
|
||||
}
|
||||
}
|
||||
},
|
||||
multiple: {
|
||||
title: "可搜索,可只选父节点",
|
||||
type: "dict-cascader",
|
||||
dict: dict({
|
||||
isTree: true,
|
||||
url: "/mock/dicts/cascaderData?multiple"
|
||||
}),
|
||||
form: {
|
||||
component: {
|
||||
showSearch: {
|
||||
filter: (inputValue, path) => {
|
||||
return path.some((option) => option.label.toLowerCase().indexOf(inputValue.toLowerCase()) > -1);
|
||||
}
|
||||
},
|
||||
"change-on-select": true
|
||||
},
|
||||
helper: "antd cascader 不支持级联多选"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<fs-page>
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding" />
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted } from "vue";
|
||||
import { useCrud } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
import { useExpose } from "@fast-crud/fast-crud";
|
||||
export default defineComponent({
|
||||
name: "ComponentCascader",
|
||||
setup() {
|
||||
// crud组件的ref
|
||||
const crudRef = ref();
|
||||
// crud 配置的ref
|
||||
const crudBinding = ref();
|
||||
// 暴露的方法
|
||||
const { expose } = useExpose({ crudRef, crudBinding });
|
||||
// 你的crud配置
|
||||
const { crudOptions } = createCrudOptions({ expose });
|
||||
// 初始化crud配置
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-unused-vars
|
||||
const { resetCrudOptions } = useCrud({ expose, crudOptions });
|
||||
// 你可以调用此方法,重新初始化crud配置
|
||||
// resetCrudOptions(options)
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
expose.doRefresh();
|
||||
});
|
||||
|
||||
return {
|
||||
crudBinding,
|
||||
crudRef
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,23 @@
|
||||
import mockUtil from "/src/mock/base";
|
||||
const options = {
|
||||
name: "ComponentCascader",
|
||||
idGenerator: 0
|
||||
};
|
||||
const list = [
|
||||
{
|
||||
cascader: ["zhinan", "shejiyuanze", "yizhi"],
|
||||
lazyLoad: ["11", "1101", "110101", "110101001"],
|
||||
multiple: ["antdv cascader不支持多选"]
|
||||
},
|
||||
{
|
||||
cascader: ["zhinan", "shejiyuanze", "yizhi"],
|
||||
multiple: ["antdv cascader不支持多选"]
|
||||
},
|
||||
{
|
||||
cascader: ["zhinan", "shejiyuanze", "yizhi"],
|
||||
multiple: ["antdv cascader不支持多选"]
|
||||
}
|
||||
];
|
||||
options.list = list;
|
||||
const mock = mockUtil.buildMock(options);
|
||||
export default mock;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user