feat: add association tab

This commit is contained in:
chenos 2020-12-25 16:15:58 +08:00
parent 32774657d7
commit 629a4de173
13 changed files with 195 additions and 49 deletions

View File

@ -2,6 +2,7 @@ import { TableOptions } from '@nocobase/database';
export default {
title: '示例',
name: 'examples',
showInDataMenu: true,
// createdBy: true,
// updatedBy: true,
@ -9,6 +10,7 @@ export default {
{
interface: 'string',
title: '单行文本',
name: 'title',
component: {
showInTable: true,
showInDetail: true,
@ -163,6 +165,18 @@ export default {
showInForm: true,
},
},
{
interface: 'linkTo',
type: 'belongsToMany',
title: '关联1',
target: 'examples',
labelField: 'title',
component: {
showInTable: true,
showInDetail: true,
showInForm: true,
},
},
{
interface: 'time',
title: '时间',

View File

@ -37,6 +37,9 @@ const api = Api.create({
},
});
api.resourcer.use(associated);
api.resourcer.registerActionHandlers({...actions.common, ...actions.associate});
api.resourcer.use(async (ctx: actions.Context, next) => {
const token = ctx.get('Authorization').replace(/^Bearer\s+/gi, '');
// console.log('user check', ctx.action.params.actionName);
@ -59,8 +62,8 @@ api.resourcer.use(async (ctx: actions.Context, next) => {
});
api.resourcer.use(async (ctx: actions.Context, next) => {
const { resourceName, fields = {} } = ctx.action.params;
const table = ctx.db.getTable(resourceName);
const { resourceField, resourceName, fields = {} } = ctx.action.params;
const table = ctx.db.getTable(resourceField ? resourceField.options.target : resourceName);
// ctx.state.developerMode = {[Op.not]: null};
ctx.state.developerMode = false;
if (table && table.hasField('developerMode') && ctx.state.developerMode === false) {
@ -89,13 +92,13 @@ api.resourcer.use(async (ctx: actions.Context, next) => {
});
api.resourcer.use(async (ctx: actions.Context, next) => {
const { resourceName, viewName, filter } = ctx.action.params;
const { resourceField, resourceName, viewName, filter } = ctx.action.params;
// TODO: 需要补充默认视图的情况
let view: any;
if (viewName) {
view = await ctx.db.getModel('views').findOne({
where: {
collection_name: resourceName,
collection_name: resourceField ? resourceField.options.target : resourceName,
name: viewName,
}
});
@ -109,9 +112,6 @@ api.resourcer.use(async (ctx: actions.Context, next) => {
await next();
});
api.resourcer.use(associated);
api.resourcer.registerActionHandlers({...actions.common, ...actions.associate});
api.registerPlugin('plugin-collections', [path.resolve(__dirname, '../../../plugin-collections'), {}]);
api.registerPlugin('plugin-pages', [path.resolve(__dirname, '../../../plugin-pages'), {}]);
api.registerPlugin('plugin-users', [path.resolve(__dirname, '../../../plugin-users'), {}]);

View File

@ -6,12 +6,13 @@ export function Create(props) {
console.log(props);
const { title, viewName, collection_name } = props.schema;
const { activeTab = {}, item = {}, associatedName, associatedKey } = props;
const { association } = activeTab;
const { associationField } = activeTab;
const params = {};
if (association) {
params['resourceName'] = association;
if (associationField && associationField.target) {
params['resourceName'] = associationField.name;
params['resourceTarget'] = associationField.target;
params['associatedName'] = associatedName;
params['associatedKey'] = associatedKey;
} else {

View File

@ -8,12 +8,13 @@ export function Filter(props) {
const [visible, setVisible] = useState(false);
const { title, viewName, collection_name } = props.schema;
const { activeTab = {}, item = {}, associatedName, associatedKey } = props;
const { association } = activeTab;
const { associationField } = activeTab;
const params = {};
if (association) {
params['resourceName'] = association;
if (associationField && associationField.target) {
params['resourceName'] = associationField.target;
params['resourceTarget'] = associationField.target;
params['associatedName'] = associatedName;
params['associatedKey'] = associatedKey;
} else {

View File

@ -5,12 +5,13 @@ import ViewFactory from '@/components/views';
export function Update(props) {
const { title, viewName, resourceName, collection_name } = props.schema;
const { resourceKey, activeTab = {}, item = {} } = props;
const { association } = activeTab;
const { associationField } = activeTab;
const params = {};
if (association) {
params['resourceName'] = association;
if (associationField && associationField.target) {
params['resourceName'] = associationField.target;
params['resourceTarget'] = associationField.target;
params['associatedName'] = resourceName;
params['associatedKey'] = item.itemId;
} else {

View File

@ -14,18 +14,34 @@ import api from '@/api-client';
import { Spin } from '@nocobase/client'
function RemoteSelectComponent(props) {
const { value, onChange, disabled, resourceName, associatedKey, labelField, valueField } = props;
const { value, onChange, disabled, resourceName, associatedKey, filter, labelField, valueField = 'id', objectValue } = props;
const { data = [], loading = true } = useRequest(() => {
return api.resource(resourceName).list({
associatedKey,
filter,
});
}, {
refreshDeps: [resourceName, associatedKey]
});
return (
<>
<Select disabled={disabled} notFoundContent={loading ? <Spin/> : undefined} allowClear loading={loading} value={value} onChange={onChange}>
{!loading && data.map(item => (<Select.Option value={item[valueField]}>{item[labelField]}</Select.Option>))}
<Select
disabled={disabled}
notFoundContent={loading ? <Spin/> : undefined}
allowClear
loading={loading}
value={value && typeof value === 'object' ? value[valueField] : value}
onChange={(value, option) => {
if (value === null || typeof value === 'undefined') {
onChange(undefined);
return;
}
// @ts-ignore
const item = option.item;
onChange(objectValue ? item : value);
}}
>
{!loading && data.map(item => (<Select.Option item={item} value={item[valueField]}>{item[labelField]}</Select.Option>))}
</Select>
</>
);

View File

@ -5,13 +5,14 @@ import { useRequest, request, Spin } from '@nocobase/client';
export function CollectionTabPane(props) {
const { loading, pageInfo = {}, activeTab = {}, item = {} } = props;
const { viewName, association, collection_name, field = {} } = activeTab;
const { sourceKey = 'id' } = field;
const { viewName, associationField = {}, collection_name, field = {} } = activeTab;
const { name, target, sourceKey = 'id' } = associationField || {};
const params = {};
if (association) {
params['resourceName'] = association;
if (target) {
params['resourceName'] = name;
params['resourceTarget'] = target;
params['associatedName'] = collection_name;
params['associatedKey'] = pageInfo[sourceKey] || item.itemId;
} else {

View File

@ -130,7 +130,7 @@ function toFlat(items = []): Array<any> {
export function DataSourceField(props: any) {
const { schema: { dataSource = [] }, value } = props;
const items = toFlat(dataSource);
console.log(items);
// console.log(items);
if (isEmpty(value)) {
return null;
}
@ -243,7 +243,7 @@ export function AttachmentFieldItem(props: any) {
exclude: ['.png', '.jpg', '.jpeg', '.gif']
})
return (
<a className={'attachment-field-item'} target={'_blank'} href={url}>
<a onClick={(e) => e.stopPropagation()} className={'attachment-field-item'} target={'_blank'} href={url}>
<img style={{marginRight: 5}} height={20} alt={title} title={title} src={img}/>
</a>
);

View File

@ -36,7 +36,7 @@ export const DrawerForm = forwardRef((props: any, ref) => {
const [resourceKey, setResourceKey] = useState(props.resourceKey);
const [visible, setVisible] = useState(false);
const name = associatedName ? `${associatedName}.${resourceName}` : resourceName;
const { data, run, loading } = useRequest((resourceKey) => {
const { data = {}, run, loading } = useRequest((resourceKey) => {
setResourceKey(resourceKey);
return api.resource(name).get({
resourceKey,
@ -89,7 +89,8 @@ export const DrawerForm = forwardRef((props: any, ref) => {
<SchemaForm
colon={true}
layout={'vertical'}
initialValues={data}
// 暂时先这么处理,如果有 associatedKey 注入表单里
initialValues={{associatedKey, ...data}}
actions={actions}
schema={{
type: 'object',

View File

@ -45,6 +45,7 @@ export default function ViewFactory(props: ViewProps) {
associatedName,
associatedKey,
resourceName,
resourceTarget,
viewName,
mode,
reference,
@ -59,9 +60,9 @@ export default function ViewFactory(props: ViewProps) {
mode
},
};
return api.resource(resourceName).getView(params);
return api.resource(resourceTarget||resourceName).getView(params);
}, {
refreshDeps: [associatedName, resourceName, viewName],
refreshDeps: [associatedName, resourceTarget, resourceName, viewName],
});
if (loading) {
return <Spin/>;

View File

@ -64,23 +64,114 @@ export default {
showInDetail: true,
showInForm: true,
"x-linkages": [
// {
// "type": "value:visible",
// "target": "association",
// "condition": "{{ $self.value === 'association' }}"
// },
{
"type": "value:visible",
"target": "association",
"condition": "{{ $self.value === 'association' }}"
type: "value:visible",
target: "associationField",
condition: "{{ $self.value === 'association' }}"
},
// {
// type: "value:schema",
// target: "association",
// condition: "{{ $self.value === 'association' }}",
// schema: {
// "x-component-props": {
// "associatedKey": "{{ $form.values && $form.values.associatedKey }}"
// },
// },
// },
{
type: "value:schema",
target: "associationField",
condition: "{{ $self.value === 'association' }}",
schema: {
"x-component-props": {
associatedKey: "{{ $form.values && $form.values.associatedKey }}"
},
},
},
],
},
},
// {
// interface: 'string',
// type: 'string',
// name: 'association',
// title: '相关数据',
// component: {
// type: 'remoteSelect',
// showInDetail: true,
// showInForm: true,
// 'x-component-props': {
// resourceName: 'collections.fields',
// labelField: 'title',
// valueField: 'name',
// filter: {
// interface: 'linkTo',
// },
// },
// },
// },
{
interface: 'linkTo',
type: 'belongsTo',
name: 'associationField',
target: 'fields',
title: '相关数据表',
labelField: 'title',
// valueField: 'name',
component: {
type: 'remoteSelect',
showInDetail: true,
showInForm: true,
'x-component-props': {
resourceName: 'collections.fields',
labelField: 'title',
// valueField: 'name',
objectValue: true,
filter: {
interface: 'linkTo',
},
},
"x-linkages": [
{
type: "value:visible",
target: "viewName",
condition: "{{ !!$self.value }}"
},
{
type: "value:schema",
target: "viewName",
condition: "{{ !!$self.value }}",
schema: {
"x-component-props": {
associatedKey: "{{ $self.value.target }}"
},
},
},
],
},
},
{
interface: 'string',
type: 'virtual',
name: 'association',
title: '相关数据表',
type: 'string',
name: 'viewName',
title: '视图',
labelField: 'title',
// valueField: 'name',
component: {
type: 'drawerSelect',
type: 'remoteSelect',
showInDetail: true,
showInForm: true,
'x-component-props': {
resourceName: 'collections.views',
labelField: 'title',
valueField: 'name',
},
},
},
{

View File

@ -166,6 +166,7 @@ export class CollectionModel extends BaseModel {
}
const Model = this.database.getModel(key);
let ids = [];
const View = this.database.getModel('views');
for (const index in data[key]) {
if (key === 'fields') {
ids = await Field.import(data[key], {
@ -204,6 +205,20 @@ export class CollectionModel extends BaseModel {
}, options);
}
if (model) {
if (key === 'tabs') {
let associationField;
if (item.association) {
associationField = await Field.findOne({
where: {
name: item.association,
collection_name: collection.name,
},
});
await model.updateAssociations({
associationField: associationField.id,
});
}
}
ids.push(model.id);
}
}

View File

@ -16,13 +16,17 @@ export default async (ctx, next) => {
plain: true,
});
collection.setDataValue('defaultViewName', defaultView.get('name'));
const tabs = await collection.getTabs({
where: {
const options = Tab.parseApiJson({
filter: {
enabled: true,
developerMode: ctx.state.developerMode,
},
order: [['sort', 'asc']],
}) as Model[];
fields: {
appends: ['associationField'],
},
sort: ['sort'],
});
const tabs = await collection.getTabs(options) as Model[];
const tabItems = [];
for (const tab of tabs) {
const itemTab = {
@ -31,15 +35,15 @@ export default async (ctx, next) => {
if (itemTab.type === 'details' && !itemTab.viewName) {
itemTab.viewName = 'details';
}
if (itemTab.type == 'association') {
itemTab.field = await collection.getFields({
where: {
name: itemTab.association,
},
limit: 1,
plain: true,
});
}
// if (itemTab.type == 'association') {
// itemTab.field = await collection.getFields({
// where: {
// name: itemTab.association,
// },
// limit: 1,
// plain: true,
// });
// }
tabItems.push(itemTab);
}
ctx.body = {