feat: 添加下载文档和显示数据按钮 (#1370)
Reviewed-on: daoyoucloud/tachybase#1370 Co-authored-by: wjh <wwwjh0710@163.com> Co-committed-by: wjh <wwwjh0710@163.com>
This commit is contained in:
parent
bc563534a3
commit
c3c8b5b655
@ -831,5 +831,10 @@
|
||||
"Load": "Load",
|
||||
"Dump": "Dump",
|
||||
"Chang on Parent": "Chang on Parent",
|
||||
"Please enter search content": "Please enter search content"
|
||||
"Please enter search content": "Please enter search content",
|
||||
"Is DownLoad":"Is DownLoad",
|
||||
"Show Data":"Show Data",
|
||||
"Setting Down Title":"Setting Down Title",
|
||||
"The current return type is not a document type":"The current return type is not a document type",
|
||||
"Test Data":"Test Data"
|
||||
}
|
||||
|
@ -934,5 +934,10 @@
|
||||
"Sorry, the page you visited does not exist.": "对不起,您访问的页面不存在。",
|
||||
"Back Home": "回到主页",
|
||||
"Chang on Parent": "选择父级",
|
||||
"Please enter search content": "请输入查询内容"
|
||||
"Please enter search content": "请输入查询内容",
|
||||
"Is DownLoad":"下载文件",
|
||||
"Show Data":"显示数据",
|
||||
"Setting Down Title":"设置文件名称",
|
||||
"The current return type is not a document type":"当前返回类型不是文档类型",
|
||||
"Test Data":"测试数据"
|
||||
}
|
||||
|
@ -787,6 +787,102 @@ export function SecondConFirm() {
|
||||
/>
|
||||
);
|
||||
}
|
||||
export function IsDownLoad() {
|
||||
const { dn } = useDesignable();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<SchemaSettingsSwitchItem
|
||||
title={t('Is DownLoad')}
|
||||
checked={!!fieldSchema?.['x-action-settings']?.onSuccess?.down}
|
||||
onChange={(value) => {
|
||||
if (!fieldSchema?.['x-action-settings']?.onSuccess) {
|
||||
fieldSchema['x-action-settings'] = {
|
||||
...fieldSchema?.['x-action-settings'],
|
||||
onSuccess: {
|
||||
down: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
fieldSchema['x-action-settings'].onSuccess.down = value;
|
||||
|
||||
dn.emit('patch', {
|
||||
schema: {
|
||||
['x-uid']: fieldSchema['x-uid'],
|
||||
'x-action-settings': { ...fieldSchema['x-action-settings'] },
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
export function ShowData() {
|
||||
const { dn } = useDesignable();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const { t } = useTranslation();
|
||||
const field = useField();
|
||||
return (
|
||||
<SchemaSettingsSwitchItem
|
||||
title={t('Show Data')}
|
||||
checked={!!fieldSchema?.['x-component-props']?.showData}
|
||||
onChange={(value) => {
|
||||
if (!fieldSchema?.['x-component-props']) {
|
||||
fieldSchema['x-component-props'] = { showData: false };
|
||||
}
|
||||
fieldSchema['x-component-props'].showData = value;
|
||||
field.componentProps = fieldSchema['x-component-props'];
|
||||
dn.emit('patch', {
|
||||
schema: {
|
||||
['x-uid']: fieldSchema['x-uid'],
|
||||
'x-component-props': { ...fieldSchema['x-component-props'] },
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function SettingDownTitle() {
|
||||
const fieldSchema = useFieldSchema();
|
||||
const { dn } = useDesignable();
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<SchemaSettingsModalItem
|
||||
title={t('Setting Down Title')}
|
||||
onSubmit={({ title }) => {
|
||||
if (!fieldSchema?.['x-action-settings']) {
|
||||
fieldSchema['x-action-settings'] = {
|
||||
onSussess: {
|
||||
downTitle: title,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
fieldSchema['x-action-settings']['onSuccess'] = {
|
||||
...fieldSchema['x-action-settings']?.['onSuccess'],
|
||||
downTitle: title,
|
||||
};
|
||||
}
|
||||
dn.emit('patch', {
|
||||
schema: {
|
||||
['x-uid']: fieldSchema['x-uid'],
|
||||
'x-action-settings': { ...fieldSchema['x-action-settings'] },
|
||||
},
|
||||
});
|
||||
}}
|
||||
schema={{
|
||||
type: 'item',
|
||||
title: t('Setting Down Title'),
|
||||
properties: {
|
||||
title: {
|
||||
'x-component': 'Input',
|
||||
default: fieldSchema['x-action-settings']?.['onSuccess']?.downTitle || 'document.docx',
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
|
@ -12,6 +12,7 @@
|
||||
"@tachybase/components": "workspace:*",
|
||||
"@tachybase/schema": "workspace:*",
|
||||
"antd": "5.19.1",
|
||||
"file-saver": "^2.0.5",
|
||||
"lodash": "~4.17.21",
|
||||
"react-i18next": "^14.1.2",
|
||||
"react-router-dom": "~6.25.1"
|
||||
|
@ -11,8 +11,11 @@ import { useField, useFieldSchema, useForm } from '@tachybase/schema';
|
||||
import { isURL } from '@tachybase/utils/client';
|
||||
|
||||
import { App } from 'antd';
|
||||
import { saveAs } from 'file-saver';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { useTranslation } from '../locale';
|
||||
|
||||
export const useCustomizeRequestActionProps = () => {
|
||||
const apiClient = useAPIClient();
|
||||
const navigate = useNavigate();
|
||||
@ -26,6 +29,7 @@ export const useCustomizeRequestActionProps = () => {
|
||||
const actionField = useField();
|
||||
const { setVisible } = useActionContext();
|
||||
const { modal, message } = App.useApp();
|
||||
const { t } = useTranslation();
|
||||
return {
|
||||
async onClick() {
|
||||
const { skipValidator, onSuccess } = actionSchema?.['x-action-settings'] ?? {};
|
||||
@ -42,9 +46,10 @@ export const useCustomizeRequestActionProps = () => {
|
||||
actionField.data ??= {};
|
||||
actionField.data.loading = true;
|
||||
try {
|
||||
await apiClient.request({
|
||||
const res = (await apiClient.request({
|
||||
url: `/customRequests:send/${fieldSchema['x-uid']}`,
|
||||
method: 'POST',
|
||||
responseType: onSuccess?.down ? 'blob' : 'json',
|
||||
data: {
|
||||
currentRecord: {
|
||||
id: record[getPrimaryKey()],
|
||||
@ -52,7 +57,16 @@ export const useCustomizeRequestActionProps = () => {
|
||||
data: formValues,
|
||||
},
|
||||
},
|
||||
});
|
||||
})) as any;
|
||||
const headerContentType = res.headers.getContentType();
|
||||
if (onSuccess?.down) {
|
||||
if (headerContentType === 'application/octet-stream') {
|
||||
const downTitle = onSuccess.downTitle;
|
||||
saveAs(res.data, downTitle);
|
||||
} else {
|
||||
message.error(t('The current return type is not a document type'));
|
||||
}
|
||||
}
|
||||
actionField.data.loading = false;
|
||||
if (!(resource instanceof TableFieldResource)) {
|
||||
__parent?.service?.refresh?.();
|
||||
@ -61,29 +75,39 @@ export const useCustomizeRequestActionProps = () => {
|
||||
if (xAction === 'customize:form:request') {
|
||||
setVisible?.(false);
|
||||
}
|
||||
if (!onSuccess?.successMessage) {
|
||||
return;
|
||||
}
|
||||
if (onSuccess?.manualClose) {
|
||||
modal.success({
|
||||
title: compile(onSuccess?.successMessage),
|
||||
onOk: async () => {
|
||||
if (onSuccess?.redirecting && onSuccess?.redirectTo) {
|
||||
if (isURL(onSuccess.redirectTo)) {
|
||||
window.location.href = onSuccess.redirectTo;
|
||||
} else {
|
||||
navigate(onSuccess.redirectTo);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
if (actionSchema?.['x-component-props']?.showData) {
|
||||
modal.confirm({
|
||||
title: 'Test Data',
|
||||
content: `${JSON.stringify(res.data)}`,
|
||||
okText: t('Confirm'),
|
||||
cancelText: t('Cancel'),
|
||||
});
|
||||
} else {
|
||||
message.success(compile(onSuccess?.successMessage));
|
||||
if (onSuccess?.redirecting && onSuccess?.redirectTo) {
|
||||
if (isURL(onSuccess.redirectTo)) {
|
||||
window.location.href = onSuccess.redirectTo;
|
||||
} else {
|
||||
navigate(onSuccess.redirectTo);
|
||||
if (!onSuccess?.successMessage) {
|
||||
return;
|
||||
}
|
||||
if (onSuccess?.manualClose) {
|
||||
modal.success({
|
||||
title: compile(onSuccess?.successMessage),
|
||||
onOk: async () => {
|
||||
if (onSuccess?.redirecting && onSuccess?.redirectTo) {
|
||||
if (isURL(onSuccess.redirectTo)) {
|
||||
window.location.href = onSuccess.redirectTo;
|
||||
} else {
|
||||
navigate(onSuccess.redirectTo);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
} else {
|
||||
message.success(compile(onSuccess?.successMessage));
|
||||
if (onSuccess?.redirecting && onSuccess?.redirectTo) {
|
||||
if (isURL(onSuccess.redirectTo)) {
|
||||
window.location.href = onSuccess.redirectTo;
|
||||
} else {
|
||||
navigate(onSuccess.redirectTo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,17 @@
|
||||
import {
|
||||
AfterSuccess,
|
||||
ButtonEditor,
|
||||
IsDownLoad,
|
||||
RemoveButton,
|
||||
SchemaSettings,
|
||||
SchemaSettingsLinkageRules,
|
||||
SecondConFirm,
|
||||
SettingDownTitle,
|
||||
ShowData,
|
||||
useCollection,
|
||||
useSchemaToolbar,
|
||||
} from '@tachybase/client';
|
||||
import { useFieldSchema } from '@tachybase/schema';
|
||||
|
||||
import { CustomRequestACL, CustomRequestSettingsItem } from './components/CustomRequestActionDesigner';
|
||||
|
||||
@ -41,6 +45,23 @@ export const customizeCustomRequestActionSettings = new SchemaSettings({
|
||||
name: 'secondConFirm',
|
||||
Component: SecondConFirm,
|
||||
},
|
||||
{
|
||||
name: 'isDownLoad',
|
||||
Component: IsDownLoad,
|
||||
},
|
||||
{
|
||||
name: 'settingDownTitle',
|
||||
Component: SettingDownTitle,
|
||||
useVisible() {
|
||||
const fieldSchema = useFieldSchema();
|
||||
const down = fieldSchema?.['x-action-settings']?.onSuccess?.down;
|
||||
return down;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'showData',
|
||||
Component: ShowData,
|
||||
},
|
||||
{
|
||||
name: 'afterSuccessfulSubmission',
|
||||
Component: AfterSuccess,
|
||||
|
@ -1,3 +1,5 @@
|
||||
import fs from 'fs';
|
||||
import http from 'http';
|
||||
import { Context, Next } from '@tachybase/actions';
|
||||
import { appendArrayColumn } from '@tachybase/evaluators';
|
||||
import { parse } from '@tachybase/utils';
|
||||
@ -160,10 +162,14 @@ export async function send(this: CustomRequestPlugin, ctx: Context, next: Next)
|
||||
);
|
||||
|
||||
try {
|
||||
ctx.body = await axios(axiosRequestConfig).then((res) => {
|
||||
this.logger.info(`custom-request:send:${filterByTk} success`);
|
||||
return res.data;
|
||||
});
|
||||
const res = await axios({ ...axiosRequestConfig, responseType: 'stream' });
|
||||
ctx.set('Content-Type', `${res.headers['content-type']}`);
|
||||
ctx.set('Content-disposition', `${res.headers['content-disposition']}`);
|
||||
this.logger.info(`custom-request:send:${filterByTk} success`);
|
||||
|
||||
const readable = res.data as http.IncomingMessage;
|
||||
|
||||
ctx.body = readable;
|
||||
} catch (err) {
|
||||
if (axios.isAxiosError(err)) {
|
||||
ctx.status = err.response?.status || 500;
|
||||
|
@ -18,7 +18,6 @@ export const generate = async (ctx: Context) => {
|
||||
},
|
||||
appends: ['template'],
|
||||
});
|
||||
console.log(template.get());
|
||||
|
||||
const rawTemplate = template.get();
|
||||
const templatePath = path.join(process.env.PWD, rawTemplate.template[0].get().url);
|
||||
@ -27,18 +26,8 @@ export const generate = async (ctx: Context) => {
|
||||
try {
|
||||
// 生成定制的 docx 文件
|
||||
const buffer = generateDocxFromTemplate(templatePath, data);
|
||||
|
||||
// 创建一个可读流
|
||||
const readableStream = new Readable({
|
||||
read(size) {
|
||||
this.push(buffer);
|
||||
this.push(null);
|
||||
},
|
||||
});
|
||||
|
||||
ctx.withoutDataWrapping = true;
|
||||
ctx.set('Content-Type', 'application/octet-stream');
|
||||
ctx.body = readableStream;
|
||||
ctx.body = buffer;
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
ctx.status = 500;
|
||||
@ -66,7 +55,6 @@ export const getTags = async (ctx: Context) => {
|
||||
|
||||
// 获取所有占位符
|
||||
const allTags = iModule.getAllTags();
|
||||
console.log('All tags in the document:', allTags);
|
||||
|
||||
// 将 tags 转换为 JSON 格式的字符串
|
||||
const tagsJson = JSON.stringify(allTags, null, 2);
|
||||
|
@ -16,6 +16,7 @@ export class PluginPrintTemplateServer extends Plugin {
|
||||
getTags,
|
||||
},
|
||||
});
|
||||
this.app.acl.allow('printTemplates', '*', 'public');
|
||||
}
|
||||
|
||||
async install() {}
|
||||
|
@ -2454,6 +2454,9 @@ importers:
|
||||
antd:
|
||||
specifier: 5.19.1
|
||||
version: 5.19.1(react-dom@18.3.1)(react@18.3.1)
|
||||
file-saver:
|
||||
specifier: ^2.0.5
|
||||
version: 2.0.5
|
||||
lodash:
|
||||
specifier: ~4.17.21
|
||||
version: 4.17.21
|
||||
|
Loading…
Reference in New Issue
Block a user