Unverified Commit 6c39ac35 authored by chenos's avatar chenos Committed by GitHub
Browse files

Develop (#68)

* refactor: fields/views/pages...

* update

* update

* update

* updates

* updates

* add yarn.lock

* updates

* updates

* updates

* updates

* updates

* updates

* updates

* updates

* updates

* developerMode

* 一大波更新

* bugfix

* fix: hide the sorting settings

* fix: reload menu when menu is updated

* 页面重构

* modify text

* 补充细节

* system settings

* 继续更新补充

* fix: 多级菜单支持

* 无限嵌套

* fix: icon

* 省市区参数调整

* 表单描述、文案调整

* 支持草稿

* 邮箱登录

* 细节补充

* 菜单页面权限初步

* 详情页打开方式

* 菜单父级、草稿问题

* 描述文字

* 详情分组显示

* 状态改为 radio

* 菜单权限

* 跳过省市区 api

* 修复权限数据范围

* onDraft

* 页面跳转

* 修改文案

* 注册、登录

* fix: 权限过滤问题

* 微调上传组件样式

* 0.4.0-alpha.0

* father-build

* remove father-build

* 细节调整
parent 068dde29
Showing with 686 additions and 343 deletions
+686 -343
......@@ -4,6 +4,7 @@ lib/
.DS_Store
package-lock.json
yarn.lock
!/yarn.lock
yarn-error.log
lerna-debug.log
packages/database/package-lock.json
......
......@@ -7,8 +7,7 @@
"start:app:server": "cd packages/app && nodemon",
"start-server": "yarn nodemon",
"bootstrap": "lerna bootstrap --no-ci",
"build": "npm run build-father-build && node packages/father-build/bin/father-build.js",
"build-father-build": "cd packages/father-build && npm run build",
"build": "father-build",
"clean": "lerna clean",
"db:start": "docker-compose up -d",
"lint": "eslint --ext .ts,.tsx,.js \"packages/*/src/**.@(ts|tsx|js)\" --fix",
......@@ -36,7 +35,7 @@
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.1",
"father-build": "^1.18.5",
"father-build": "^1.19.2",
"jest": "^26.1.0",
"koa": "^2.13.0",
"koa-bodyparser": "^4.3.0",
......
{
"name": "@nocobase/actions",
"version": "0.3.0-alpha.0",
"version": "0.4.0-alpha.0",
"description": "",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
......@@ -8,8 +8,8 @@
},
"license": "MIT",
"dependencies": {
"@nocobase/database": "^0.3.0-alpha.0",
"@nocobase/resourcer": "^0.3.0-alpha.0"
"@nocobase/database": "^0.4.0-alpha.0",
"@nocobase/resourcer": "^0.4.0-alpha.0"
},
"devDependencies": {
"koa": "^2.13.0",
......
......@@ -286,6 +286,7 @@ export async function get(ctx: Context, next: Next) {
resourceKeyAttribute,
fields = []
} = ctx.action.params;
console.log({associated, resourceField})
if (associated && resourceField) {
const AssociatedModel = ctx.db.getModel(associatedName);
if (!(associated instanceof AssociatedModel)) {
......@@ -297,7 +298,16 @@ export async function get(ctx: Context, next: Next) {
fields,
});
if (resourceField instanceof HASONE || resourceField instanceof BELONGSTO) {
const model: Model = await associated[getAccessor]({ ...options, context: ctx });
let model: Model = await associated[getAccessor]({ context: ctx });
if (model) {
model = await TargetModel.findOne({
...options,
context: ctx,
where: {
[TargetModel.primaryKeyAttribute]: model[TargetModel.primaryKeyAttribute],
},
});
}
ctx.body = model;
} else if (resourceField instanceof HASMANY || resourceField instanceof BELONGSTOMANY) {
const [model]: Model[] = await associated[getAccessor]({
......
{
"name": "@nocobase/app",
"version": "0.3.0-alpha.0",
"version": "0.4.0-alpha.0",
"private": true,
"scripts": {
"start": "concurrently \"nodemon\" \"umi dev\"",
"start-api": "nodemon",
"db-migrate": "ts-node ./src/api/migrate.ts",
"build": "father-build && umi build",
"build": "umi build",
"postinstall": "umi generate tmp",
"prettier": "prettier --write '**/*.{js,jsx,tsx,ts,less,md,json}'",
"test": "umi-test",
......@@ -26,18 +26,18 @@
"dependencies": {
"@ant-design/pro-layout": "^5.0.12",
"@formily/antd-components": "^1.3.6",
"@nocobase/client": "^0.3.0-alpha.0",
"@nocobase/database": "^0.3.0-alpha.0",
"@nocobase/father-build": "^0.3.0-alpha.0",
"@nocobase/plugin-action-logs": "^0.3.0-alpha.0",
"@nocobase/plugin-china-region": "^0.3.0-alpha.0",
"@nocobase/plugin-collections": "^0.3.0-alpha.0",
"@nocobase/plugin-pages": "^0.3.0-alpha.0",
"@nocobase/server": "^0.3.0-alpha.0",
"@nocobase/client": "^0.4.0-alpha.0",
"@nocobase/database": "^0.4.0-alpha.0",
"@nocobase/plugin-action-logs": "^0.4.0-alpha.0",
"@nocobase/plugin-china-region": "^0.4.0-alpha.0",
"@nocobase/plugin-collections": "^0.4.0-alpha.0",
"@nocobase/plugin-pages": "^0.4.0-alpha.0",
"@nocobase/server": "^0.4.0-alpha.0",
"@types/react-big-calendar": "^0.24.8",
"@umijs/preset-react": "1.x",
"@umijs/test": "^3.2.23",
"ahooks": "^2.9.3",
"antd": "^4.13.0",
"array-move": "^3.0.1",
"clean-deep": "^3.4.0",
"concurrently": "^5.3.0",
......@@ -48,6 +48,7 @@
"react": "16.14.0",
"react-big-calendar": "^0.30.0",
"react-dom": "16.14.0",
"react-drag-listview": "^0.1.8",
"react-image-lightbox": "^5.1.1",
"react-sortable-hoc": "^1.11.0",
"styled-components": "^5.2.1",
......
......@@ -40,6 +40,7 @@ class ApiClient {
options.params = restParams;
} else {
options.method = 'post';
options.params = restParams;
options.data = values;
}
if (associatedKey) {
......
import api from '../app';
import Database from '@nocobase/database';
import views_v2 from '@nocobase/plugin-pages/src/collections/views_v2';
import { Op } from 'sequelize';
(async () => {
await api.loadPlugins();
const database: Database = api.database;
await api.database.sync();
await api.database.getModel('collections').load({skipExisting: true});
const [Collection, Field, Page, Menu, View] = database.getModels(['collections', 'fields', 'pages_v2', 'menus', 'views_v2']);
// await Collection.import(require('./collections/authors').default, { update: true });
// await Collection.import(require('./collections/books').default, { update: true });
await Menu.truncate();
await Page.destroy({
where: {
id: {
[Op.not]: null,
},
}
});
await View.destroy({
where: {
id: {
[Op.not]: null,
},
}
});
const collection = await Collection.findOne({
where: {
name: 'views_v2',
}
});
await collection.updateAssociations({
fields: views_v2.fields,
views_v2: views_v2.views_v2,
});
// const authors = require('./collections/authors').default;
console.log('views_v2.fields', views_v2.fields.map(field=>field.name));
// await collection.updateAssociations({
// views_v2: authors.views_v2,
// });
const tables = database.getTables([
'collections',
'fields',
'menus',
'pages_v2',
'views_v2',
'pages_views_v2',
'automations',
'automations_jobs',
'action_logs',
'users',
'roles',
'scopes',
'views_actions_v2',
'views_pages_v2',
'views_fields_v2',
]);
for (let table of tables) {
console.log(table.getName());
await Collection.import(table.getOptions(), { update: true, migrate: false });
}
const menus = [
{
title: '仪表盘',
icon: 'DashboardOutlined',
type: 'group',
children: [
{
title: '欢迎光临',
icon: 'DatabaseOutlined',
type: 'page',
pageName: 'welcome'
},
],
},
{
title: '数据',
icon: 'DatabaseOutlined',
type: 'group',
children: [
{
title: '作者',
icon: 'DatabaseOutlined',
type: 'page',
pageName: 'authors.all',
},
{
title: '申请表单',
icon: 'DatabaseOutlined',
type: 'page',
pageName: 'authors.form',
},
],
},
{
title: '用户',
icon: 'TeamOutlined',
type: 'group',
children: [
{
title: '用户管理',
icon: 'DatabaseOutlined',
type: 'page',
pageName: 'users.all',
},
],
},
{
title: '动态',
icon: 'NotificationOutlined',
type: 'group',
children: [
{
title: '操作日志',
icon: 'DatabaseOutlined',
type: 'page',
pageName: 'action_logs.all',
},
],
},
{
title: '配置',
icon: 'SettingOutlined',
type: 'group',
children: [
{
title: '数据表配置',
icon: 'DatabaseOutlined',
type: 'page',
pageName: 'collections.all',
},
{
title: '菜单配置',
icon: 'MenuOutlined',
type: 'page',
pageName: 'menus.all',
},
{
title: '页面配置',
icon: 'MenuOutlined',
type: 'page',
pageName: 'pages_v2.globals',
},
{
title: '权限配置',
icon: 'MenuOutlined',
type: 'page',
pageName: 'roles.all',
},
{
title: '自动化配置',
icon: 'MenuOutlined',
type: 'page',
pageName: 'automations.all',
},
],
},
];
for (const item of menus) {
const menu = await Menu.create(item);
await menu.updateAssociations(item);
}
})();
......@@ -39,17 +39,19 @@ export default {
showInForm: true,
},
},
// {
// interface: 'linkTo',
// title: '书籍',
// target: 'books',
// labelField: 'name',
// component: {
// showInTable: true,
// showInDetail: true,
// showInForm: true,
// },
// },
{
interface: 'linkTo',
type: 'belongsToMany',
title: '书籍',
name: 'books',
target: 'books',
labelField: 'name',
component: {
showInTable: true,
showInDetail: true,
showInForm: true,
},
},
{
interface: 'createdBy',
title: '创建人',
......@@ -87,4 +89,87 @@ export default {
},
}
],
views_v2: [
{
type: 'table',
name: 'table',
title: '全部数据',
labelField: 'name',
actions: [
{
name: 'filter',
type: 'filter',
title: '过滤',
fields: [
'name',
],
// viewName: 'form',
},
{
name: 'create',
type: 'create',
title: '新增',
viewName: 'form',
},
{
name: 'destroy',
type: 'destroy',
title: '删除',
},
],
fields: ['name'],
detailsOpenMode: 'drawer', // window
details: ['descriptions', 'books'],
sort: ['id'],
},
{
type: 'descriptions',
name: 'descriptions',
title: '详情',
fields: ['name'],
actions: [
{
name: 'update',
type: 'update',
title: '编辑',
viewName: 'form',
},
],
},
{
type: 'form',
name: 'form',
title: '表单',
fields: ['name'],
},
{
type: 'association',
name: 'books',
title: '书籍',
targetViewName: 'table',
targetFieldName: 'books',
},
],
pages_v2: [
{
title: '全部数据',
name: 'all',
views: ['table'],
},
{
title: '详情',
name: 'descriptions',
views: ['descriptions'],
},
{
title: '表单',
name: 'form',
views: ['form'],
},
{
title: '书籍',
name: 'books',
views: ['books'],
},
],
};
......@@ -74,4 +74,75 @@ export default {
},
}
],
views_v2: [
{
type: 'table',
name: 'table',
title: '全部数据',
labelField: 'name',
actions: [
{
name: 'filter',
type: 'filter',
title: '过滤',
fields: [
'name',
],
// viewName: 'form',
},
{
name: 'create',
type: 'create',
title: '新增',
viewName: 'form',
},
{
name: 'destroy',
type: 'destroy',
title: '删除',
},
],
fields: ['name'],
detailsOpenMode: 'drawer', // window
details: ['descriptions', 'form'],
sort: ['id'],
},
{
type: 'descriptions',
name: 'descriptions',
title: '详情',
fields: ['name'],
actions: [
{
name: 'update',
type: 'update',
title: '编辑',
viewName: 'form',
},
],
},
{
type: 'form',
name: 'form',
title: '表单',
fields: ['name'],
},
],
pages_v2: [
{
title: '全部数据',
name: 'all',
views: ['table'],
},
{
title: '详情',
name: 'descriptions',
views: ['descriptions'],
},
{
title: '表单',
name: 'form',
views: ['form'],
},
],
};
......@@ -17,134 +17,15 @@ const data = [
type: 'layout',
template: 'TopMenuLayout',
sort: 10,
children: [
{
title: '仪表盘',
type: 'page',
path: '/dashboard',
icon: 'DashboardOutlined',
template: 'page1',
sort: 20,
showInMenu: true,
},
{
title: '数据',
type: 'layout',
path: '/collections',
icon: 'DatabaseOutlined',
template: 'SideMenuLayout',
sort: 30,
showInMenu: true,
children: [
// {
// title: '页面3',
// type: 'page',
// path: '/collections/page3',
// icon: 'dashboard',
// template: 'page3',
// sort: 40,
// },
// {
// title: '页面4',
// type: 'page',
// path: '/collections/page4',
// icon: 'dashboard',
// template: 'page4',
// sort: 50,
// },
]
},
{
title: '用户',
type: 'layout',
path: '/users',
icon: 'TeamOutlined',
template: 'SideMenuLayout',
sort: 70,
showInMenu: true,
children: [
{
title: '用户管理',
type: 'collection',
path: '/users/users',
icon: 'UserOutlined',
template: 'collection',
collection: 'users',
sort: 80,
showInMenu: true,
},
]
},
{
title: '动态',
type: 'layout',
path: '/activity',
icon: 'NotificationOutlined',
template: 'SideMenuLayout',
sort: 85,
showInMenu: true,
children: [
{
title: '操作记录',
type: 'collection',
path: '/activity/logs',
icon: 'HistoryOutlined',
template: 'collection',
collection: 'action_logs',
sort: 80,
showInMenu: true,
},
]
},
{
title: '配置',
type: 'layout',
path: '/settings',
icon: 'SettingOutlined',
template: 'SideMenuLayout',
sort: 90,
showInMenu: true,
children: [
{
title: '页面与菜单',
type: 'collection',
collection: 'pages',
path: '/settings/pages',
icon: 'MenuOutlined',
sort: 100,
developerMode: true,
showInMenu: true,
},
{
title: '数据表配置',
type: 'collection',
collection: 'collections',
path: '/settings/collections',
icon: 'TableOutlined',
sort: 110,
showInMenu: true,
},
{
title: '权限组配置',
type: 'collection',
collection: 'roles',
path: '/settings/roles',
icon: 'TableOutlined',
sort: 120,
showInMenu: true,
},
{
title: '自动化配置',
type: 'collection',
collection: 'automations',
path: '/settings/automations',
icon: 'TableOutlined',
sort: 130,
showInMenu: true,
},
]
},
],
redirect: '/admin',
},
{
title: '后台',
path: '/admin',
type: 'page',
inherit: false,
template: 'AdminLoader',
order: 230,
},
{
title: '登录页面',
......@@ -161,7 +42,7 @@ const data = [
inherit: false,
template: 'register',
order: 130,
}
},
];
(async () => {
......@@ -190,6 +71,7 @@ const data = [
nickname: "超级管理员",
password: "admin",
username: "admin",
email: 'dev@nocobase.com',
token: "38979f07e1fca68fb3d2",
});
}
......@@ -221,14 +103,16 @@ const data = [
});
}
const Role = database.getModel('roles');
const roles = await Role.bulkCreate([
{ title: '系统开发组', type: -1 },
{ title: '匿名用户组', type: 0 },
{ title: '普通用户组', default: true },
]);
await roles[0].updateAssociations({
users: user
});
if (Role) {
const roles = await Role.bulkCreate([
{ title: '系统开发组', type: -1 },
// { title: '匿名用户组', type: 0 },
{ title: '普通用户组', default: true },
]);
await roles[0].updateAssociations({
users: user
});
}
const Action = database.getModel('actions');
// 全局
......@@ -238,8 +122,116 @@ const data = [
// 导入地域数据
await chinaRegionSeederInit(api);
await database.getModel('collections').import(require('./collections/example').default);
await database.getModel('collections').import(require('./collections/authors').default);
await database.getModel('collections').import(require('./collections/books').default);
// await database.getModel('collections').import(require('./collections/example').default);
// await database.getModel('collections').import(require('./collections/authors').default);
// await database.getModel('collections').import(require('./collections/books').default);
const Menu = database.getModel('menus');
const menus = [
{
title: '仪表盘',
icon: 'DashboardOutlined',
type: 'group',
children: [
{
title: '欢迎光临',
icon: 'DatabaseOutlined',
type: 'page',
views: [],
name: 'welcome',
},
],
},
{
title: '数据',
icon: 'DatabaseOutlined',
type: 'group',
children: [],
},
{
title: '用户',
icon: 'TeamOutlined',
type: 'group',
children: [
{
title: '用户管理',
icon: 'DatabaseOutlined',
type: 'page',
views: ['users.table'],
name: 'users',
},
],
},
{
title: '动态',
icon: 'NotificationOutlined',
type: 'group',
developerMode: true,
children: [
{
title: '操作日志',
icon: 'DatabaseOutlined',
type: 'page',
views: ['action_logs.table'],
developerMode: true,
name: 'auditing',
},
],
},
{
title: '配置',
icon: 'SettingOutlined',
type: 'group',
developerMode: true,
children: [
{
name: 'system_settings',
title: '系统配置',
icon: 'DatabaseOutlined',
type: 'page',
views: ['system_settings.descriptions'],
developerMode: true,
},
{
name: 'collections',
title: '数据表配置',
icon: 'DatabaseOutlined',
type: 'page',
views: ['collections.table'],
developerMode: true,
},
{
name: 'menus',
title: '菜单和页面配置',
icon: 'MenuOutlined',
type: 'page',
views: ['menus.table'],
developerMode: true,
},
{
name: 'permissions',
title: '权限配置',
icon: 'MenuOutlined',
type: 'page',
views: ['roles.table'],
developerMode: true,
},
{
name: 'automations',
title: '自动化配置',
icon: 'MenuOutlined',
type: 'page',
views: ['automations.table'],
developerMode: true,
},
],
},
];
for (const item of menus) {
const menu = await Menu.create(item);
await menu.updateAssociations(item);
}
await database.close();
})();
......@@ -35,6 +35,9 @@ export const request: RequestConfig = {
export async function getInitialState() {
const { pathname, search } = location;
console.log(location);
const { data: systemSettings = {} } = await umiRequest('/system_settings:get?fields[appends]=logo,logo.storage', {
method: 'get',
});
let redirect = '';
// if (href.includes('?')) {
redirect = `?redirect=${pathname}${search}`;
......@@ -49,11 +52,13 @@ export async function getInitialState() {
if (!data.id) {
history.push('/login' + redirect);
return {
systemSettings,
currentUser: {},
};
}
return {
systemSettings,
currentUser: data,
};
} catch (error) {
......@@ -62,5 +67,8 @@ export async function getInitialState() {
}
}
return {};
return {
systemSettings,
currentUser: {},
};
}
\ No newline at end of file
.action-buttons {
display: flex;
justify-content: flex-end;
// justify-content: flex-end;
min-height: 32px;
flex-direction: row-reverse;
align-items: flex-start;
.action-button {
margin-right: 8px;
&:last-child {
margin-right: 0;
}
margin-left: 8px;
}
.filter-action-button {
position: absolute;
......
......@@ -24,17 +24,40 @@ export const Cascader = connect({
})(function (props) {
const {
disabled,
target,
labelField,
valueField = 'id',
parentField,
maxLevel,
changeOnSelect,
// target,
// labelField,
// valueField = 'id',
// parentField,
// maxLevel,
// changeOnSelect,
value = [],
onChange,
schema = {},
// TODO(feature): 增加静态数据支持
// dataSource: []
} = props;
const {
target,
targetKey: valueField,
// 值字段
// valueField: 'code',
// 名称字段
labelField,
// TODO(refactor): 等 toWhere 重构完成后要改成 parent
// 上级字段名
parentField,
maxLevel,
// valueField = 'id',
// 深度限制,默认:-1(代表不控制,即如果是数据表,则无限加载)
// limit: -1,
// 可选层级,默认:-1(代表可选的最深层级)
// maxLevel: null,
// 是否可以不选择到最深一级
// 'x-component-props': { changeOnSelect: true }
incompletely: changeOnSelect,
} = schema;
const fieldNames = {
label: labelField,
value: valueField,
......
import React, { useState } from 'react'
import { connect } from '@formily/react-schema-renderer'
import { Select, Drawer, Button, Space } from 'antd'
import { Select, Button, Space } from 'antd'
import {
mapStyledProps,
mapTextComponent,
......@@ -9,13 +9,15 @@ import {
isArr
} from '../shared'
import ViewFactory from '@/components/views'
import Drawer from '@/components/pages/AdminLoader/Drawer';
import View from '@/components/pages/AdminLoader/View';
function transform({value, multiple, labelField, valueField = 'id'}) {
let selectedKeys = [];
let selectedValue = [];
const values = Array.isArray(value) ? value : [];
selectedKeys = values.map(item => item[valueField]);
selectedValue = values.map(item => {
const values = Array.isArray(value) ? value : [value];
selectedKeys = values.filter(item => item).map(item => item[valueField]);
selectedValue = values.filter(item => item).map(item => {
return {
value: item[valueField],
label: item[labelField],
......@@ -27,14 +29,13 @@ function transform({value, multiple, labelField, valueField = 'id'}) {
return [selectedKeys, selectedValue];
}
function DrawerSelectComponent(props) {
const { disabled, target, multiple, filter, resourceName, associatedKey, labelField, valueField = 'id', value, onChange } = props;
export function DrawerSelectComponent(props) {
const { __parent, size, schema = {}, disabled, viewName, target, multiple, filter, resourceName, associatedKey, labelField, valueField = 'id', value, onChange } = props;
const [selectedKeys, selectedValue] = transform({value, multiple, labelField, valueField });
const [visible, setVisible] = useState(false);
const [selectedRowKeys, setSelectedRowKeys] = useState(multiple ? selectedKeys : [selectedKeys]);
const [selectedRows, setSelectedRows] = useState(selectedValue);
const [options, setOptions] = useState(selectedValue);
// console.log('valuevaluevaluevaluevaluevalue', value);
const { title = '' } = schema;
return (
<>
<Select
......@@ -42,6 +43,7 @@ function DrawerSelectComponent(props) {
open={false}
mode={multiple ? 'tags' : undefined}
labelInValue
size={size}
allowClear={true}
value={options}
notFoundContent={''}
......@@ -63,58 +65,54 @@ function DrawerSelectComponent(props) {
}}
onClick={() => {
if (!disabled) {
setVisible(true);
Drawer.open({
title: `选择要关联的${title}数据`,
content: ({resolve}) => {
console.log('valuevaluevaluevaluevaluevalue', selectedRowKeys, selectedRows, options);
const [rows, setRows] = useState(selectedRows);
const [rowKeys, setRowKeys] = useState(selectedRowKeys)
const [selected, setSelected] = useState(Array.isArray(value) ? value : [value]);
console.log({selectedRowKeys});
return (
<>
<View
__parent={__parent}
associatedKey={associatedKey}
multiple={multiple}
defaultFilter={filter}
defaultSelectedRowKeys={selectedRowKeys}
onSelected={(values) => {
setSelected(values);
const [selectedKeys, selectedValue] = transform({value: values, multiple: true, labelField, valueField });
setSelectedRows(selectedValue);
setRows(selectedValue);
setSelectedRowKeys(selectedKeys);
setRowKeys(selectedKeys);
console.log({ values, selectedValue, selectedKeys });
console.log({selectedRows, selectedRowKeys});
}}
viewName={viewName || `${target}.table`}
/>
<Drawer.Footer>
<Space>
<Button onClick={resolve}>取消</Button>
<Button onClick={() => {
setOptions(rows);
// console.log('valuevaluevaluevaluevaluevalue', {selectedRowKeys});
onChange(multiple ? selected : selected.shift());
// console.log({rows, rowKeys});
resolve();
}} type={'primary'}>确定</Button>
</Space>
</Drawer.Footer>
</>
)
},
})
// setVisible(true);
}
}}
></Select>
<Drawer
width={'40%'}
className={'noco-drawer'}
title={'关联数据'}
visible={visible}
bodyStyle={{padding: 0}}
onClose={() => {
setVisible(false);
}}
footer={[
<div
style={{
textAlign: 'right',
}}
>
<Space>
<Button onClick={() => setVisible(false)}>取消</Button>
<Button type={'primary'} onClick={() => {
setOptions(selectedRows);
// console.log('valuevaluevaluevaluevaluevalue', {selectedRowKeys});
onChange(multiple ? selectedRowKeys : selectedRowKeys.shift());
setVisible(false);
}}>确定</Button>
</Space>
</div>
]}
>
<ViewFactory
defaultFilter={filter}
multiple={multiple}
resourceTarget={target}
resourceName={associatedKey ? resourceName : target}
associatedKey={associatedKey}
isFieldComponent={true}
selectedRowKeys={selectedRowKeys}
onSelected={(values) => {
// 需要返回的是 array
const [selectedKeys, selectedValue] = transform({value: values, multiple: true, labelField, valueField });
setSelectedRows(selectedValue);
setSelectedRowKeys(selectedKeys);
// console.log('valuevaluevaluevaluevaluevalue', {values, selectedKeys, selectedValue});
}}
// associatedKey={}
// associatedName={associatedName}
viewName={'table'}
/>
</Drawer>
</>
);
}
......
......@@ -480,7 +480,7 @@ export const Filter = connect({
],
};
const { value, onChange, associatedKey, filter = {}, sourceName, sourceFilter = {}, fields = [], ...restProps } = props;
console.log('filter', {associatedKey})
const { data = [], loading = true } = useRequest(() => {
return associatedKey ? api.resource(`collections.fields`).list({
associatedKey,
......
......@@ -6,12 +6,14 @@ import { markdown } from '@/components/views/Field'
export const FormDescription = createVirtualBox(
'description',
styled(({ children, className, ...props }) => {
styled(({ schema = {}, children, className, ...props }) => {
const { title, tooltip } = schema as any;
console.log({schema})
return (
<Card size={'small'} headStyle={{padding: 0}} bodyStyle={{
<Card title={title} size={'small'} headStyle={{padding: 0}} bodyStyle={{
padding: 0,
}} className={className} {...props}>
{typeof children === 'string' && children && <div dangerouslySetInnerHTML={{__html: markdown(children)}}></div>}
{typeof tooltip === 'string' && tooltip && <div dangerouslySetInnerHTML={{__html: markdown(tooltip)}}></div>}
</Card>
)
})`
......
......@@ -9,6 +9,7 @@ import findIndex from 'lodash/findIndex';
import get from 'lodash/get';
import set from 'lodash/set';
import { Scope } from './Scope';
import { DrawerSelectComponent } from '../drawer-select';
export const Permissions = {} as {Actions: any, Fields: any, Tabs: any};
......@@ -70,11 +71,15 @@ Permissions.Actions = connect({
}
const values = [...value||[]];
const index = findIndex(values, (item: any) => item && item.name === `${resourceKey}:${record.name}`);
console.log(values, index, `${resourceKey}:${record.name}`, get(values, [index, 'scope']));
return (
<Scope
resourceTarget={'scopes'}
associatedName={'collections'}
<DrawerSelectComponent
schema={{
title: '选择可操作的数据范围',
}}
size={'small'}
associatedKey={resourceKey}
viewName={'collections.scopes.table'}
target={'scopes'}
multiple={false}
labelField={'title'}
......@@ -86,16 +91,41 @@ Permissions.Actions = connect({
if (index === -1) {
values.push({
name: `${resourceKey}:${record.name}`,
scope_id: data,
scope_id: data.id,
});
} else {
set(values, [index, 'scope_id'], data);
set(values, [index, 'scope_id'], data.id);
}
console.log('valvalvalvalval', {values})
onChange(values);
console.log('valvalvalvalval', data);
}}
/>
// <Scope
// resourceTarget={'scopes'}
// associatedName={'collections'}
// associatedKey={resourceKey}
// target={'scopes'}
// multiple={false}
// labelField={'title'}
// valueField={'id'}
// value={get(values, [index, 'scope'])}
// onChange={(data) => {
// const values = [...value||[]];
// const index = findIndex(values, (item: any) => item && item.name === `${resourceKey}:${record.name}`);
// if (index === -1) {
// values.push({
// name: `${resourceKey}:${record.name}`,
// scope_id: data,
// });
// } else {
// set(values, [index, 'scope_id'], data);
// }
// console.log('valvalvalvalval', {values})
// onChange(values);
// console.log('valvalvalvalval', data);
// }}
// />
)
}
},
......
......@@ -24,6 +24,8 @@ import { Permissions } from './permissions'
import { DraggableTable } from './draggable-table'
import { Values } from './values'
import { Automations } from './automations'
import { VirtualTable } from './virtual-table'
import { Wysiwyg } from './wysiwyg'
export const setup = () => {
registerFormFields({
......@@ -44,6 +46,7 @@ export const setup = () => {
month: DatePicker.MonthPicker,
week: DatePicker.WeekPicker,
string: Input,
select: Input,
icon: Icon,
textarea: Input.TextArea,
number: NumberPicker,
......@@ -60,11 +63,13 @@ export const setup = () => {
subTable: SubTable,
draggableTable: DraggableTable,
values: Values,
wysiwyg: Wysiwyg,
'permissions.actions': Permissions.Actions,
'permissions.fields': Permissions.Fields,
'permissions.tabs': Permissions.Tabs,
'automations.datetime': Automations.DateTime,
'automations.endmode': Automations.EndMode,
'automations.cron': Automations.Cron,
'virtualTable': VirtualTable,
});
}
......@@ -12,9 +12,26 @@ import {
import { useRequest } from 'umi';
import api from '@/api-client';
import { Spin } from '@nocobase/client'
import get from 'lodash/get';
function RemoteSelectComponent(props) {
const { value, onChange, disabled, resourceName, associatedKey, filter, labelField, valueField = 'id', objectValue, placeholder, multiple } = props;
let { schema = {}, value, onChange, disabled, resourceName, associatedKey, filter, labelField, valueField, objectValue, placeholder, multiple } = props;
console.log({schema});
if (!resourceName) {
resourceName = get(schema, 'component.resourceName');
}
if (!filter) {
filter = get(schema, 'component.filter');
}
if (!labelField) {
labelField = get(schema, 'component.labelField');
}
if (!valueField) {
valueField = get(schema, 'component.valueField');
}
if (!valueField) {
valueField = 'id';
}
const { data = [], loading = true } = useRequest(() => {
return api.resource(resourceName).list({
associatedKey,
......@@ -27,6 +44,7 @@ function RemoteSelectComponent(props) {
if (multiple) {
selectProps.mode = 'multiple'
}
console.log({ data, props, associatedKey })
return (
<>
<Select
......
......@@ -9,6 +9,7 @@ import { components, fields2columns } from '@/components/views/SortableTable';
import Form from './Form';
import { Spin } from '@nocobase/client';
import maxBy from 'lodash/maxBy';
import View from '@/components/pages/AdminLoader/View';
export interface SimpleTableProps {
schema?: any;
......@@ -24,102 +25,20 @@ export function generateIndex(): string {
}
export default function Table(props: SimpleTableProps) {
console.log(props);
const drawerRef = useRef<any>();
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
const onTableChange = (selectedRowKeys: React.ReactText[]) => {
setSelectedRowKeys(selectedRowKeys);
}
const tableProps: any = {};
tableProps.rowSelection = {
selectedRowKeys,
onChange: onTableChange,
}
const { rowKey = '__id', fields = [] } = props;
const { target = 'fields', value = [], onChange } = props;
const { data: schema = {}, loading } = useRequest(() => api.resource(target).getView({
resourceKey: 'simple'
}));
const [dataSource, setDataSource] = useState(value.map((item, index) => {
if (item.__id) {
return item;
};
return {...item, __id: generateIndex()};
}))
if (loading) {
return <Spin/>
}
// console.log('dataSource', dataSource);
const { schema = {}, associatedKey, value, onChange, __parent } = props;
console.log({props, associatedKey, schema, __parent})
const { collection_name, name } = schema;
const viewName = `${collection_name}.${name}.table`;
return (
<div>
<div>
<Space style={{marginBottom: 14, position: 'absolute', right: 0, top: -31}}>
<Popconfirm title="确认删除吗?" onConfirm={() => {
console.log({selectedRowKeys})
const newValues = dataSource.filter(item => selectedRowKeys.indexOf(item.__id) === -1);
setDataSource(newValues);
onChange(newValues);
}}>
<Button size={'small'} type={'ghost'} danger>删除</Button>
</Popconfirm>
<Button size={'small'} type={'primary'} onClick={() => {
drawerRef.current.setVisible(true);
drawerRef.current.setIndex(undefined);
drawerRef.current.setData({});
drawerRef.current.setTitle('新增子字段');
}}>新增</Button>
</Space>
</div>
<Form target={target} onFinish={(values, index: number) => {
console.log(values);
const newVaules = [...dataSource];
if (typeof index === 'undefined') {
newVaules.push({...values, __id: generateIndex()})
} else {
newVaules[index] = values;
}
setDataSource(newVaules);
onChange(newVaules);
// console.log(newVaules);
}} ref={drawerRef}/>
<AntdTable
size={'small'}
rowKey={rowKey}
// loading={loading}
columns={fields2columns(schema.fields||[])}
dataSource={dataSource}
onChange={(pagination, filters, sorter, extra) => {
}}
components={components({
data: {
list: dataSource,
},
mutate: (values) => {
onChange(values.list);
setDataSource(values.list);
console.log('mutate', values);
},
rowKey,
onMoved: async ({resourceKey, target}) => {
}
})}
onRow={(record, index) => ({
onClick: () => {
console.log(record);
drawerRef.current.setVisible(true);
drawerRef.current.setIndex(index);
drawerRef.current.setData(record);
drawerRef.current.setTitle('编辑子字段');
}
})}
pagination={false}
{...tableProps}
<>
<View
__parent={__parent}
data={value}
onChange={onChange}
associatedKey={associatedKey}
viewName={viewName}
type={'subTable'}
/>
</div>
);
</>
)
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment