🔱: [client] sync upgrade with 4 commits [trident-sync]

perf: upload sdk换成aws-s3
feat: upload 支持s3 minio

https://github.com/fast-crud/fast-crud/issues/149
feat: fs-form-wrapper支持多实例

https://github.com/fast-crud/fast-crud/issues/150
pull/14/head
GitHub Actions Bot 2023-03-10 19:24:05 +00:00
parent 52a167c647
commit 336faa46b2
22 changed files with 514 additions and 189 deletions

View File

@ -22,6 +22,7 @@
"dependencies": {
"@ant-design/colors": "^7.0.0",
"@ant-design/icons-vue": "^6.1.0",
"@aws-sdk/s3-request-presigner": "^3.288.0",
"@fast-crud/fast-crud": "workspace:^1.9.2",
"@fast-crud/fast-extends": "workspace:^1.9.2",
"@fast-crud/ui-antdv": "workspace:^1.9.2",

View File

@ -1,6 +1,8 @@
<template>
<a-config-provider :locale="locale">
<router-view v-if="routerEnabled" />
<fs-form-provider>
<router-view v-if="routerEnabled" />
</fs-form-provider>
</a-config-provider>
</template>

View File

@ -169,6 +169,28 @@ function install(app: any, options: any = {}) {
},
domain: "http://d2p.file.handsfree.work/"
},
s3: {
bucket: "fast-crud",
sdkOpts: {
s3ForcePathStyle: true,
signatureVersion: "v4",
region: "us-east-1",
forcePathStyle: true,
endpoint: "https://play.min.io",
credentials: {
accessKeyId: "Q3AM3UQ867SPQQA43P2F", //访问登录名
secretAccessKey: "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG" //访问密码
}
},
custom: {
// buildKey获取授权等接口中将会传入
},
successHandle(ret: any) {
// 上传完成后可以在此处处理结果修改url什么的
console.log("success handle:", ret);
return ret;
}
},
form: {
action: "http://www.docmirror.cn:7070/api/upload/form/upload",
name: "file",

View File

@ -255,6 +255,12 @@ export const crudResources = [
path: "/crud/component/uploader/qiniu",
component: "/crud/component/uploader/qiniu/index.vue"
},
{
title: "s3上传",
name: "ComponentUploaderS3",
path: "/crud/component/uploader/s3",
component: "/crud/component/uploader/s3/index.vue"
},
{
title: "富文本编辑器",
name: "ComponentEditor",

View File

@ -52,3 +52,11 @@ h1, h2, h3, h4, h5, h6 {
.ml-5{
margin-left:5px;
}
.mt-10{
margin-top:10px;
}
.m-10{
margin:10px;
}

View File

@ -1,7 +1,7 @@
import { requestForMock } from "/src/api/service";
const request = requestForMock;
const apiPrefix = "/mock/BasisColumnsSet";
export function GetList(query) {
export function GetList(query: any) {
return request({
url: apiPrefix + "/page",
method: "get",
@ -9,7 +9,7 @@ export function GetList(query) {
});
}
export function AddObj(obj) {
export function AddObj(obj: any) {
return request({
url: apiPrefix + "/add",
method: "post",
@ -17,7 +17,7 @@ export function AddObj(obj) {
});
}
export function UpdateObj(obj) {
export function UpdateObj(obj: any) {
return request({
url: apiPrefix + "/update",
method: "post",
@ -25,7 +25,7 @@ export function UpdateObj(obj) {
});
}
export function DelObj(id) {
export function DelObj(id: any) {
return request({
url: apiPrefix + "/delete",
method: "post",
@ -33,7 +33,7 @@ export function DelObj(id) {
});
}
export function GetObj(id) {
export function GetObj(id: any) {
return request({
url: apiPrefix + "/info",
method: "get",
@ -41,7 +41,7 @@ export function GetObj(id) {
});
}
export function BatchDelete(ids) {
export function BatchDelete(ids: any) {
return request({
url: apiPrefix + "/batchDelete",
method: "post",

View File

@ -1,19 +1,18 @@
import * as api from "./api";
import { dict } from "@fast-crud/fast-crud";
import { ref } from "vue";
export default function ({ expose }) {
const pageRequest = async (query) => {
import * as api from "./api.js";
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
export default function ({ expose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
return await api.GetList(query);
};
const editRequest = async ({ form, row }) => {
const editRequest = async ({ form, row }: EditReq) => {
form.id = row.id;
return await api.UpdateObj(form);
};
const delRequest = async ({ row }) => {
const delRequest = async ({ row }: DelReq) => {
return await api.DelObj(row.id);
};
const addRequest = async ({ form }) => {
const addRequest = async ({ form }: AddReq) => {
return await api.AddObj(form);
};
return {

View File

@ -16,30 +16,19 @@
</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";
import { defineComponent, onMounted } from "vue";
import createCrudOptions from "./crud";
import { useFs } from "@fast-crud/fast-crud";
import { message } from "ant-design-vue";
export default defineComponent({
name: "BasisColumnsSet",
setup() {
// crudref
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)
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions });
//
onMounted(() => {
expose.doRefresh();
crudExpose.doRefresh();
});
function columnsSetToggleMode() {

View File

@ -10,7 +10,7 @@ import { useCrud } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
import { useExpose } from "@fast-crud/fast-crud";
export default defineComponent({
name: "AliossUploader",
name: "QiniuUploader",
setup() {
// crudref
const crudRef = ref();

View File

@ -0,0 +1,53 @@
import { requestForMock } from "/src/api/service";
import { generateUrls } from "/@/views/crud/component/uploader/s3/s3-server";
const request = requestForMock;
const apiPrefix = "/mock/S3Uploader";
export function GetList(query: any) {
return request({
url: apiPrefix + "/page",
method: "get",
data: query
});
}
export function AddObj(obj: any) {
return request({
url: apiPrefix + "/add",
method: "post",
data: obj
});
}
export function UpdateObj(obj: any) {
return request({
url: apiPrefix + "/update",
method: "post",
data: obj
});
}
export function DelObj(id: any) {
return request({
url: apiPrefix + "/delete",
method: "post",
params: { id }
});
}
export function GetObj(id: any) {
return request({
url: apiPrefix + "/info",
method: "get",
params: { id }
});
}
/**
* url
* @param bucket
* @param key
* @constructor
*/
export async function GetSignedUrl(bucket: string, key: string) {
return await generateUrls(bucket, key);
}

View File

@ -0,0 +1,86 @@
import * as api from "./api";
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
import { GetSignedUrl } from "./api";
export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
return await api.GetList(query);
};
const editRequest = async ({ form, row }: EditReq) => {
form.id = row.id;
return await api.UpdateObj(form);
};
const delRequest = async ({ row }: DelReq) => {
return await api.DelObj(row.id);
};
const addRequest = async ({ form }: AddReq) => {
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
}
},
file: {
title: "S3上传",
type: "file-uploader",
form: {
component: {
uploader: {
type: "s3"
}
}
}
},
pictureCard: {
title: "照片墙",
type: "image-uploader",
form: {
component: {
uploader: {
type: "s3"
},
valueType: "key"
}
},
column: {
component: {
async buildUrl(key: string) {
const url = await GetSignedUrl("fast-crud", key);
debugger;
console.log("url", url);
return url;
}
}
}
},
cropper: {
title: "裁剪",
type: "cropper-uploader",
form: {
component: {
uploader: {
type: "s3"
}
}
}
}
}
}
};
}

View File

@ -0,0 +1,36 @@
<template>
<fs-page>
<template #header>
<div class="title">
s3上传
<span class="sub">支持minio</span>
</div>
<div class="more">
<a target="_blank" href="http://fast-crud.docmirror.cn/api/crud-options/toolbar.html#columnsfilter-mode">文档</a>
</div>
</template>
<fs-crud ref="crudRef" v-bind="crudBinding" />
</fs-page>
</template>
<script lang="ts">
import { defineComponent, onMounted } from "vue";
import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud.js";
export default defineComponent({
name: "S3Uploader",
setup() {
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions });
//
onMounted(() => {
crudExpose.doRefresh();
});
return {
crudBinding,
crudRef
};
}
});
</script>

View File

@ -0,0 +1,23 @@
// @ts-ignore
import mockUtil from "/src/mock/base";
const options: any = {
name: "S3Uploader",
idGenerator: 0
};
const list = [
{
avatar: "http://greper.handsfree.work/extends/avatar.jpg",
file: ["http://greper.handsfree.work/extends/avatar.jpg", "https://www.baidu.com/img/bd_logo1.png"],
image: ["http://greper.handsfree.work/extends/avatar.jpg", "https://www.baidu.com/img/bd_logo1.png"],
image2: ["http://greper.handsfree.work/extends/avatar.jpg", "https://www.baidu.com/img/bd_logo1.png"]
},
{
radio: "2"
},
{
radio: "0"
}
];
options.list = list;
const mock = mockUtil.buildMock(options);
export default mock;

View File

@ -0,0 +1,29 @@
import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
export async function generateUrls(bucket: string, key: string) {
const client = new S3Client({
s3ForcePathStyle: true,
signatureVersion: "v4",
region: "us-east-1",
forcePathStyle: true,
endpoint: "https://play.min.io",
credentials: {
accessKeyId: "Q3AM3UQ867SPQQA43P2F", //访问登录名
secretAccessKey: "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG" //访问密码
}
});
const getParams = {
Bucket: bucket,
Key: key
};
let url;
const getCmd = new GetObjectCommand(getParams);
try {
url = await getSignedUrl(client, getCmd);
} catch (err) {
console.log("Error getting signed URL ", err);
}
return url;
}

View File

@ -1,7 +1,7 @@
import { requestForMock } from "/src/api/service";
const request = requestForMock;
const apiPrefix = "/mock/FormCustomForm";
export function GetList(query) {
export function GetList(query: any) {
return request({
url: apiPrefix + "/page",
method: "get",
@ -9,7 +9,7 @@ export function GetList(query) {
});
}
export function AddObj(obj) {
export function AddObj(obj: any) {
return request({
url: apiPrefix + "/add",
method: "post",
@ -17,7 +17,7 @@ export function AddObj(obj) {
});
}
export function UpdateObj(obj) {
export function UpdateObj(obj: any) {
return request({
url: apiPrefix + "/update",
method: "post",
@ -25,7 +25,7 @@ export function UpdateObj(obj) {
});
}
export function DelObj(id) {
export function DelObj(id: any) {
return request({
url: apiPrefix + "/delete",
method: "post",

View File

@ -1,40 +0,0 @@
import * as api from "./api";
import { dict } from "@fast-crud/fast-crud";
export default function ({ expose }) {
const { getFormRef, getFormData } = 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: {
title: {
title: "商品标题",
type: "text"
},
code: {
title: "商品代码",
search: { show: true },
type: "text"
}
}
}
};
}

View File

@ -0,0 +1,134 @@
import * as api from "./api.js";
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, CrudExpose, CrudOptions, DelReq, dict, EditReq, useColumns, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
import { message } from "ant-design-vue";
import { useFormWrapper } from "@fast-crud/fast-crud/src";
function useCustomFormWrapperDemo(crudExpose: CrudExpose) {
let index = 0;
// 自定义表单配置
const { buildFormOptions } = useColumns();
const customOptions: CrudOptions = {
columns: {
index: {
title: "index",
type: "text"
},
customField: {
title: "新表单字段",
type: "text"
},
groupField: {
title: "分组字段",
type: "text"
}
},
form: {
wrapper: {
title: "自定义表单",
buttons: {
open: {
text: "打开新对话框",
order: -1,
click() {
openCustomForm();
}
}
}
},
group: {
groups: {
testGroupName: {
header: "分组测试",
columns: ["groupField"]
}
}
},
doSubmit({ form }) {
console.log("form submit:", form);
message.info("自定义表单提交:" + JSON.stringify(form));
message.warn("抛出异常可以阻止表单关闭");
throw new Error("抛出异常可以阻止表单关闭");
}
}
};
const { openDialog } = useFormWrapper();
//使用crudOptions结构来构建自定义表单配置
//打开自定义表单
const openCustomForm = async () => {
const formOptions = buildFormOptions(customOptions);
index++;
formOptions.initialForm = { index };
formOptions.newInstance = true; //新实例打开
const dialogRef = await openDialog(formOptions);
console.log("openCustomFormRef", dialogRef);
};
const openCustomFormByExpose = async () => {
const formOptions = buildFormOptions(customOptions);
index++;
formOptions.initialForm = { index };
formOptions.newInstance = true; //新实例打开
const dialogRef = await crudExpose.openDialog(formOptions);
console.log("openCustomFormByExposeRef", dialogRef);
};
return {
openCustomForm,
openCustomFormByExpose
};
}
export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const { openCustomForm, openCustomFormByExpose } = useCustomFormWrapperDemo(crudExpose);
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
return await api.GetList(query);
};
const editRequest = async ({ form, row }: EditReq) => {
form.id = row.id;
return await api.UpdateObj(form);
};
const delRequest = async ({ row }: DelReq) => {
return await api.DelObj(row.id);
};
const addRequest = async ({ form }: AddReq) => {
return await api.AddObj(form);
};
return {
crudOptions: {
actionbar: {
buttons: {
custom: {
text: "打开自定义对话框",
async click() {
await openCustomForm();
}
},
byExpose: {
text: "byExpose.openDialog",
async click() {
await openCustomFormByExpose();
}
}
}
},
request: {
pageRequest,
addRequest,
editRequest,
delRequest
},
columns: {
title: {
title: "商品标题",
type: "text"
},
code: {
title: "商品代码",
search: { show: true },
type: "text"
}
}
}
};
}

View File

@ -1,85 +1,33 @@
<template>
<fs-page>
<fs-crud ref="crudRef" v-bind="crudBinding">
<template #actionbar-right>
<a-divider type="vertical" />
<a-button @click="openCustomForm"></a-button>
</template>
</fs-crud>
<template #header>
<div class="title">自定义表单</div>
<div class="more">
<a target="_blank" href="http://fast-crud.docmirror.cn/api/use.html#useformwrapper">文档</a>
</div>
</template>
<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
</fs-page>
</template>
<script>
import { defineComponent, ref, onMounted } from "vue";
import { useCrud, useExpose, useColumns } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
import _ from "lodash-es";
import { message } from "ant-design-vue";
<script lang="ts">
import { defineComponent, onMounted } from "vue";
import { useFs, UseFsProps } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud.js";
export default defineComponent({
name: "FormCustomForm",
setup(props, ctx) {
// crudref
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)
//
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions } as UseFsProps);
onMounted(() => {
expose.doRefresh();
crudExpose.doRefresh();
});
//
const { buildFormOptions } = useColumns();
const customOptions = {
columns: {
customField: {
title: "新表单字段",
type: "text"
},
groupField: {
title: "分组字段",
type: "text"
}
},
form: {
wrapper: { title: "自定义表单" },
group: {
groups: {
testGroupName: {
header: "分组测试",
columns: ["groupField"]
}
}
},
doSubmit({ form }) {
console.log("form submit:", form);
message.info("自定义表单提交:" + JSON.stringify(form));
message.warn("抛出异常可以阻止表单关闭");
throw new Error("抛出异常可以阻止表单关闭");
}
}
};
//使crudOptions
const formOptions = buildFormOptions(customOptions);
//
const openCustomForm = () => {
expose.getFormWrapperRef().open(formOptions);
};
return {
crudBinding,
crudRef,
openCustomForm
crudRef
};
}
});

View File

@ -1,3 +1,5 @@
import { message } from "ant-design-vue";
export default function ({ expose }) {
return {
crudOptions: {
@ -10,6 +12,13 @@ export default function ({ expose }) {
console.log("onOpened", e);
}
},
doSubmit({ form }) {
//
console.log("form submit:", form);
message.info("自定义表单提交:" + JSON.stringify(form));
message.warn("抛出异常可以阻止表单关闭");
throw new Error("抛出异常可以阻止表单关闭");
},
labelCol: { span: 6 },
wrapperCol: { span: 16 },
helper: {

View File

@ -1,41 +1,55 @@
<template>
<fs-page>
<a-row :gutter="10">
<a-col :span="12">
<a-card title="直接显示表单">
<fs-form ref="formRef" v-bind="formOptions"> </fs-form>
<div style="margin-top: 10px">
<a-button @click="formSubmit"></a-button>
<a-button @click="formReset"></a-button>
<a-button class="ml-10" @click="setFormDataTest">setFormData</a-button>
</div>
</a-card>
</a-col>
<a-col span="12">
<a-card title="打开表单对话框">
<a-button @click="openFormWrapper"></a-button>
<fs-form-wrapper ref="formWrapperRef" v-bind="formWrapperOptions" />
</a-card>
<template #header>
<div class="title">独立使用表单</div>
<div class="more">
<a target="_blank" href="http://fast-crud.docmirror.cn/api/use.html#useformwrapper">文档</a>
</div>
</template>
<div style="padding: 20px">
<a-row :gutter="10">
<a-col :span="12">
<a-card class="mt-10" title="直接显示表单">
<fs-form ref="formRef" v-bind="formOptions"> </fs-form>
<div style="margin-top: 10px">
<a-button @click="formSubmit"></a-button>
<a-button @click="formReset"></a-button>
<a-button class="ml-10" @click="setFormDataTest">setFormData</a-button>
</div>
</a-card>
</a-col>
<a-col span="12">
<a-card class="mt-10" title="直接打开对话框,无需写 fs-form-wrapper 标签">
<div style="margin-top: 10px">
<a-button @click="openFormWrapperNoTag"></a-button>
</div>
</a-card>
<a-card class="mt-10" title="打开表单对话框">
<a-button @click="openFormWrapper"></a-button>
<fs-form-wrapper ref="formWrapperRef" v-bind="formWrapperOptions" />
</a-card>
<a-card title="打开表单对话框复用crudOptions">
<a-button @click="openFormWrapper2"></a-button>
<fs-form-wrapper ref="formWrapper2Ref" v-bind="formWrapper2Options" />
</a-card>
<a-card class="mt-10" title="打开表单对话框复用crudOptions">
<a-button @click="openFormWrapper2"></a-button>
<fs-form-wrapper ref="formWrapper2Ref" v-bind="formWrapper2Options" />
</a-card>
<a-card class="mt-10" title="打开表单对话框【复用crudBinding】">
<a-button @click="openFormWrapper2"></a-button>
<fs-form-wrapper ref="formWrapperRef2" v-bind="formWrapperOptions2" />
</a-card>
</a-col>
</a-row>
<a-card class="mt-10" title="打开表单对话框【复用crudBinding】">
<a-button @click="openFormWrapper2"></a-button>
<fs-form-wrapper ref="formWrapperRef2" v-bind="formWrapperOptions2" />
</a-card>
</a-col>
</a-row>
</div>
</fs-page>
</template>
<script>
import { defineComponent, ref } from "vue";
import { message } from "ant-design-vue";
import { useCrud, useExpose, useColumns } from "@fast-crud/fast-crud";
import { useCrud, useExpose, useColumns, useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
import { useFormWrapper } from "@fast-crud/fast-crud/src";
function createFormOptionsFromCrudOptions() {
const { buildFormOptions } = useColumns();
@ -126,7 +140,7 @@ function useFormDirect() {
* 表单对话框独立使用
* @returns {{formWrapperRef, formWrapperOptions, openFormWrapper: openFormWrapper}}
*/
function useFormWrapper() {
function useFormWrapperUsingTag() {
const formWrapperRef = ref();
const formWrapperOptions = ref();
formWrapperOptions.value = createFormOptions();
@ -148,19 +162,7 @@ function useFormWrapper() {
function useCrudBindingForm() {
const formWrapperRef2 = ref();
// crudref
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)
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions });
// == crudBinding.addForm crudBinding.editForm
const formWrapperOptions2 = ref({
@ -201,14 +203,32 @@ function useCrudOptions() {
};
}
/**
* 无需写 fs-form-wrapper标签直接打开对话框
* 此方式可以层叠打开多个对话框
*/
function useFormProvider() {
const { openDialog } = useFormWrapper();
async function openFormWrapperNoTag() {
const opts = createFormOptionsFromCrudOptions();
const wrapperRef = await openDialog(opts);
console.log("对话框已打开", wrapperRef);
}
return {
openFormWrapperNoTag
};
}
export default defineComponent({
name: "FormIndependent",
setup() {
return {
...useFormDirect(),
...useFormWrapper(),
...useFormWrapperUsingTag(),
...useCrudBindingForm(),
...useCrudOptions()
...useCrudOptions(),
...useFormProvider()
};
}
});