diff --git a/packages/app/src/api/collections/example.ts b/packages/app/src/api/collections/example.ts
index 8bd99f6a9c..aa10638394 100644
--- a/packages/app/src/api/collections/example.ts
+++ b/packages/app/src/api/collections/example.ts
@@ -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: '时间',
diff --git a/packages/app/src/api/index.ts b/packages/app/src/api/index.ts
index b3ee0bf610..521adbc696 100644
--- a/packages/app/src/api/index.ts
+++ b/packages/app/src/api/index.ts
@@ -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'), {}]);
diff --git a/packages/app/src/components/actions/Create.tsx b/packages/app/src/components/actions/Create.tsx
index 1aecd56e98..a533b9a2a1 100644
--- a/packages/app/src/components/actions/Create.tsx
+++ b/packages/app/src/components/actions/Create.tsx
@@ -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 {
diff --git a/packages/app/src/components/actions/Filter.tsx b/packages/app/src/components/actions/Filter.tsx
index 9dc00fd8e8..1503c1c4cd 100644
--- a/packages/app/src/components/actions/Filter.tsx
+++ b/packages/app/src/components/actions/Filter.tsx
@@ -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 {
diff --git a/packages/app/src/components/actions/Update.tsx b/packages/app/src/components/actions/Update.tsx
index e837fd5220..d5510f4fa5 100644
--- a/packages/app/src/components/actions/Update.tsx
+++ b/packages/app/src/components/actions/Update.tsx
@@ -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 {
diff --git a/packages/app/src/components/form.fields/remote-select/index.tsx b/packages/app/src/components/form.fields/remote-select/index.tsx
index 07bbaa1dc8..103aefb8fc 100644
--- a/packages/app/src/components/form.fields/remote-select/index.tsx
+++ b/packages/app/src/components/form.fields/remote-select/index.tsx
@@ -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 (
<>
- : undefined} allowClear loading={loading} value={value} onChange={onChange}>
- {!loading && data.map(item => ({item[labelField]}))}
+ : 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 => ({item[labelField]}))}
>
);
diff --git a/packages/app/src/components/pages/CollectionLoader/CollectionTabPane.tsx b/packages/app/src/components/pages/CollectionLoader/CollectionTabPane.tsx
index c19a7e3c0e..d09ff5120c 100644
--- a/packages/app/src/components/pages/CollectionLoader/CollectionTabPane.tsx
+++ b/packages/app/src/components/pages/CollectionLoader/CollectionTabPane.tsx
@@ -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 {
diff --git a/packages/app/src/components/views/Field/index.tsx b/packages/app/src/components/views/Field/index.tsx
index 25b219ace6..b670daa6e4 100644
--- a/packages/app/src/components/views/Field/index.tsx
+++ b/packages/app/src/components/views/Field/index.tsx
@@ -130,7 +130,7 @@ function toFlat(items = []): Array {
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 (
-
+ e.stopPropagation()} className={'attachment-field-item'} target={'_blank'} href={url}>
);
diff --git a/packages/app/src/components/views/Form/DrawerForm.tsx b/packages/app/src/components/views/Form/DrawerForm.tsx
index 5be26b1de7..bdce618d11 100644
--- a/packages/app/src/components/views/Form/DrawerForm.tsx
+++ b/packages/app/src/components/views/Form/DrawerForm.tsx
@@ -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) => {
;
diff --git a/packages/plugin-collections/src/collections/tabs.ts b/packages/plugin-collections/src/collections/tabs.ts
index c92d0ed04f..29dbd23e90 100644
--- a/packages/plugin-collections/src/collections/tabs.ts
+++ b/packages/plugin-collections/src/collections/tabs.ts
@@ -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',
+ },
},
},
{
diff --git a/packages/plugin-collections/src/models/collection.ts b/packages/plugin-collections/src/models/collection.ts
index c1cbe47073..771364eec2 100644
--- a/packages/plugin-collections/src/models/collection.ts
+++ b/packages/plugin-collections/src/models/collection.ts
@@ -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);
}
}
diff --git a/packages/plugin-pages/src/actions/getCollection.ts b/packages/plugin-pages/src/actions/getCollection.ts
index 9d6c0c272e..4677204263 100644
--- a/packages/plugin-pages/src/actions/getCollection.ts
+++ b/packages/plugin-pages/src/actions/getCollection.ts
@@ -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 = {