[improvement][project] global refactor , code format , support llm , support fuzzy detect ,support query filter and so on.

This commit is contained in:
lexluo
2023-07-08 15:00:03 +08:00
parent 5ffd617431
commit 404163f391
329 changed files with 21050 additions and 5036 deletions

View File

@@ -1,23 +1,31 @@
import type { ActionType, ProColumns } from '@ant-design/pro-table';
import ProTable from '@ant-design/pro-table';
import { message, Button, Drawer, Space, Popconfirm } from 'antd';
import React, { useRef, useState } from 'react';
import { message, Button, Drawer, Space, Popconfirm, Modal, Card, Row, Col } from 'antd';
import { ConsoleSqlOutlined, CoffeeOutlined } from '@ant-design/icons';
import React, { useRef, useState, useEffect } from 'react';
import type { Dispatch } from 'umi';
import { connect } from 'umi';
import DataSourceCreateForm from '../Datasource/components/DataSourceCreateForm';
import ClassDataSourceTypeModal from './ClassDataSourceTypeModal';
import type { StateType } from '../model';
import { getDatasourceList, deleteDatasource } from '../service';
import DataSource from '../Datasource';
import moment from 'moment';
const { Meta } = Card;
type Props = {
dispatch: Dispatch;
domainManger: StateType;
};
const ClassDataSourceTable: React.FC<Props> = ({ dispatch, domainManger }) => {
const { selectDomainId } = domainManger;
const { selectDomainId, dataBaseResultColsMap, dataBaseConfig } = domainManger;
const [createModalVisible, setCreateModalVisible] = useState<boolean>(false);
const [dataSourceItem, setDataSourceItem] = useState<any>();
const [createDataSourceModalOpen, setCreateDataSourceModalOpen] = useState(false);
const [dataSourceModalVisible, setDataSourceModalVisible] = useState(false);
const [fastModeSql, setFastModeSql] = useState<string>('');
const [fastModeTableName, setFastModeTableName] = useState<string>('');
const actionRef = useRef<ActionType>();
@@ -62,6 +70,10 @@ const ClassDataSourceTable: React.FC<Props> = ({ dispatch, domainManger }) => {
key="classEditBtn"
onClick={() => {
setDataSourceItem(record);
if (record.datasourceDetail.queryType === 'table_query') {
setDataSourceModalVisible(true);
return;
}
setCreateModalVisible(true);
}}
>
@@ -72,12 +84,12 @@ const ClassDataSourceTable: React.FC<Props> = ({ dispatch, domainManger }) => {
okText="是"
cancelText="否"
onConfirm={async () => {
const { code } = await deleteDatasource(record.id);
const { code, msg } = await deleteDatasource(record.id);
if (code === 200) {
setDataSourceItem(undefined);
actionRef.current?.reload();
} else {
message.error('删除失败');
message.error(msg);
}
}}
>
@@ -121,6 +133,20 @@ const ClassDataSourceTable: React.FC<Props> = ({ dispatch, domainManger }) => {
return resData;
};
const queryDataBaseExcuteSql = (tableName: string) => {
const sql = `select * from ${tableName}`;
setFastModeSql(sql);
setFastModeTableName(tableName);
dispatch({
type: 'domainManger/queryDataBaseExcuteSql',
payload: {
sql,
domainId: selectDomainId,
tableName,
},
});
};
return (
<>
<ProTable
@@ -140,13 +166,46 @@ const ClassDataSourceTable: React.FC<Props> = ({ dispatch, domainManger }) => {
type="primary"
onClick={() => {
setDataSourceItem(undefined);
setCreateModalVisible(true);
setCreateDataSourceModalOpen(true);
}}
>
</Button>,
]}
/>
{
<ClassDataSourceTypeModal
open={createDataSourceModalOpen}
onTypeChange={(type) => {
if (type === 'fast') {
setDataSourceModalVisible(true);
} else {
setCreateModalVisible(true);
}
setCreateDataSourceModalOpen(false);
}}
/>
}
{dataSourceModalVisible && (
<DataSourceCreateForm
sql={fastModeSql}
basicInfoFormMode="fast"
domainId={Number(selectDomainId)}
dataSourceItem={dataSourceItem}
onCancel={() => {
setDataSourceModalVisible(false);
}}
onDataBaseTableChange={(tableName: string) => {
queryDataBaseExcuteSql(tableName);
}}
onSubmit={() => {
setDataSourceModalVisible(false);
setDataSourceItem(undefined);
actionRef.current?.reload();
}}
createModalVisible={dataSourceModalVisible}
/>
)}
{createModalVisible && (
<Drawer
width={'100%'}

View File

@@ -0,0 +1,68 @@
import { Modal, Card, Row, Col } from 'antd';
import { ConsoleSqlOutlined, CoffeeOutlined } from '@ant-design/icons';
import React, { useState, useEffect } from 'react';
const { Meta } = Card;
type Props = {
open: boolean;
onTypeChange: (type: 'fast' | 'normal') => void;
};
const ClassDataSourceTypeModal: React.FC<Props> = ({ open, onTypeChange }) => {
const [createDataSourceModalOpen, setCreateDataSourceModalOpen] = useState(false);
useEffect(() => {
setCreateDataSourceModalOpen(open);
}, [open]);
return (
<>
<Modal
open={createDataSourceModalOpen}
onCancel={() => {
setCreateDataSourceModalOpen(false);
}}
footer={null}
centered
closable={false}
>
<Row gutter={16} style={{ marginTop: '0px' }}>
<Col span={12}>
<Card
hoverable
style={{ height: 220 }}
onClick={() => {
onTypeChange('fast');
setCreateDataSourceModalOpen(false);
}}
cover={
<CoffeeOutlined
width={240}
style={{ paddingTop: '45px', height: 120, fontSize: '48px', color: '#1890ff' }}
/>
}
>
<Meta title="快速创建" description="自动进行数据源可视化创建" />
</Card>
</Col>
<Col span={12}>
<Card
onClick={() => {
onTypeChange('normal');
setCreateDataSourceModalOpen(false);
}}
hoverable
style={{ height: 220 }}
cover={
<ConsoleSqlOutlined
style={{ paddingTop: '45px', height: 120, fontSize: '48px', color: '#1890ff' }}
/>
}
>
<Meta title="SQL脚本" description="自定义SQL脚本创建数据源" />
</Card>
</Col>
</Row>
</Modal>
</>
);
};
export default ClassDataSourceTypeModal;

View File

@@ -88,6 +88,10 @@ const ClassDimensionTable: React.FC<Props> = ({ domainManger, dispatch }) => {
dataIndex: 'name',
title: '维度名称',
},
{
dataIndex: 'alias',
title: '别名',
},
{
dataIndex: 'bizName',
title: '字段名称',
@@ -146,12 +150,12 @@ const ClassDimensionTable: React.FC<Props> = ({ domainManger, dispatch }) => {
okText="是"
cancelText="否"
onConfirm={async () => {
const { code } = await deleteDimension(record.id);
const { code, msg } = await deleteDimension(record.id);
if (code === 200) {
setDimensionItem(undefined);
actionRef.current?.reload();
} else {
message.error('删除失败');
message.error(msg);
}
}}
>

View File

@@ -68,6 +68,10 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
dataIndex: 'name',
title: '指标名称',
},
{
dataIndex: 'alias',
title: '别名',
},
{
dataIndex: 'bizName',
title: '字段名称',
@@ -118,12 +122,12 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
okText="是"
cancelText="否"
onConfirm={async () => {
const { code } = await deleteMetric(record.id);
const { code, msg } = await deleteMetric(record.id);
if (code === 200) {
setMetricItem(undefined);
actionRef.current?.reload();
} else {
message.error('删除失败');
message.error(msg);
}
}}
>

View File

@@ -7,29 +7,39 @@ import { formLayout } from '@/components/FormHelper/utils';
import styles from '../style.less';
type Props = {
domainId: number;
dataBaseConfig: any;
onSubmit: (params?: any) => void;
};
const FormItem = Form.Item;
const TextArea = Input.TextArea;
const DatabaseCreateForm: ForwardRefRenderFunction<any, Props> = ({ domainId }, ref) => {
const DatabaseCreateForm: ForwardRefRenderFunction<any, Props> = (
{ domainId, dataBaseConfig, onSubmit },
ref,
) => {
const [form] = Form.useForm();
const [selectedDbType, setSelectedDbType] = useState<string>('h2');
const queryDatabaseConfig = async () => {
const { code, data } = await getDatabaseByDomainId(domainId);
if (code === 200) {
form.setFieldsValue({ ...data });
setSelectedDbType(data?.type);
return;
}
message.error('数据库配置获取错误');
};
// const queryDatabaseConfig = async () => {
// const { code, data } = await getDatabaseByDomainId(domainId);
// if (code === 200) {
// form.setFieldsValue({ ...data });
// setSelectedDbType(data?.type);
// return;
// }
// message.error('数据库配置获取错误');
// };
useEffect(() => {
form.resetFields();
queryDatabaseConfig();
}, [domainId]);
form.setFieldsValue({ ...dataBaseConfig });
setSelectedDbType(dataBaseConfig?.type);
}, [dataBaseConfig]);
// useEffect(() => {
// form.resetFields();
// // queryDatabaseConfig();
// }, [domainId]);
const getFormValidateFields = async () => {
return await form.validateFields();
@@ -48,6 +58,7 @@ const DatabaseCreateForm: ForwardRefRenderFunction<any, Props> = ({ domainId },
if (code === 200) {
message.success('保存成功');
onSubmit?.();
return;
}
message.error(msg);

View File

@@ -11,8 +11,8 @@ type Props = {
domainManger: StateType;
};
const DatabaseSection: React.FC<Props> = ({ domainManger }) => {
const { selectDomainId } = domainManger;
const DatabaseSection: React.FC<Props> = ({ domainManger, dispatch }) => {
const { selectDomainId, dataBaseConfig } = domainManger;
const entityCreateRef = useRef<any>({});
@@ -22,8 +22,16 @@ const DatabaseSection: React.FC<Props> = ({ domainManger }) => {
<ProCard title="数据库设置" bordered>
<DatabaseCreateForm
ref={entityCreateRef}
dataBaseConfig={dataBaseConfig}
domainId={Number(selectDomainId)}
onSubmit={() => {}}
onSubmit={() => {
dispatch({
type: 'domainManger/queryDatabaseByDomainId',
payload: {
domainId: selectDomainId,
},
});
}}
/>
</ProCard>
</Space>

View File

@@ -85,6 +85,7 @@ const DimensionInfoModal: React.FC<CreateFormProps> = ({
>
<Input placeholder="名称不可重复" disabled={isEdit} />
</FormItem>
<FormItem
name="datasourceId"
label="所属数据源"
@@ -98,6 +99,9 @@ const DimensionInfoModal: React.FC<CreateFormProps> = ({
))}
</Select>
</FormItem>
<FormItem name="alias" label="别名">
<Input placeholder="多个别名用英文逗号隔开" />
</FormItem>
<FormItem
name="semanticType"
label="类型"

View File

@@ -2,6 +2,8 @@ import React, { useEffect, useState } from 'react';
import { Button, Modal, message } from 'antd';
import { addDomainExtend, editDomainExtend, getDomainExtendDetailConfig } from '../../service';
import DimensionMetricVisibleTransfer from './DimensionMetricVisibleTransfer';
import { exChangeRichEntityListToIds } from './utils';
type Props = {
domainId: number;
themeData: any;
@@ -36,7 +38,6 @@ const DimensionMetricVisibleModal: React.FC<Props> = ({
onSubmit,
}) => {
const [sourceList, setSourceList] = useState<any[]>([]);
const [visibilityData, setVisibilityData] = useState<any>({});
const [selectedKeyList, setSelectedKeyList] = useState<string[]>([]);
const settingTypeConfig = settingType === 'dimension' ? dimensionConfig : metricConfig;
useEffect(() => {
@@ -47,27 +48,12 @@ const DimensionMetricVisibleModal: React.FC<Props> = ({
setSourceList(list);
}, [settingSourceList]);
const queryThemeListData: any = async () => {
const { code, data } = await getDomainExtendDetailConfig({
domainId,
});
if (code === 200) {
setVisibilityData(data.visibility);
return;
}
message.error('获取可见信息失败');
};
useEffect(() => {
queryThemeListData();
}, []);
useEffect(() => {
setSelectedKeyList(visibilityData?.[settingTypeConfig.visibleIdListKey] || []);
}, [visibilityData]);
setSelectedKeyList(themeData.visibility?.[settingTypeConfig.visibleIdListKey] || []);
}, [themeData]);
const saveEntity = async () => {
const { id } = themeData;
const { id, entity } = themeData;
let saveDomainExtendQuery = addDomainExtend;
if (id) {
saveDomainExtendQuery = editDomainExtend;
@@ -79,6 +65,8 @@ const DimensionMetricVisibleModal: React.FC<Props> = ({
}
return list;
}, []);
const entityParams = exChangeRichEntityListToIds(entity);
themeData.entity = entityParams;
const params = {
...themeData,
visibility: themeData.visibility || {},

View File

@@ -2,15 +2,17 @@ import { useEffect, useState, forwardRef, useImperativeHandle } from 'react';
import type { ForwardRefRenderFunction } from 'react';
import { message, Form, Input, Select, Button } from 'antd';
import { addDomainExtend, editDomainExtend } from '../../service';
import type { ISemantic, IChatConfig } from '../../data';
import { formLayout } from '@/components/FormHelper/utils';
import { exChangeRichEntityListToIds } from './utils';
import styles from '../style.less';
type Props = {
entityData: any;
metricList: any[];
dimensionList: any[];
entityData: IChatConfig.IEntity;
metricList: ISemantic.IMetricList;
dimensionList: ISemantic.IDimensionList;
domainId: number;
onSubmit: (params?: any) => void;
onSubmit: () => void;
};
const FormItem = Form.Item;
@@ -34,33 +36,19 @@ const EntityCreateForm: ForwardRefRenderFunction<any, Props> = (
if (Object.keys(entityData).length === 0) {
return;
}
const { detailData = {}, names = [] } = entityData;
if (!detailData.dimensionIds) {
entityData = {
...entityData,
detailData: {
...detailData,
dimensionIds: [],
},
};
}
if (!detailData.metricIds) {
entityData = {
...entityData,
detailData: {
...detailData,
metricIds: [],
},
};
}
form.setFieldsValue({ ...entityData, name: names.join(',') });
const names = entityData.names || [];
const formatEntityData = exChangeRichEntityListToIds(entityData);
form.setFieldsValue({
...formatEntityData,
name: names.join(','),
});
}, [entityData]);
useImperativeHandle(ref, () => ({
getFormValidateFields,
}));
useEffect(() => {
const metricOption = metricList.map((item: any) => {
const metricOption = metricList.map((item: ISemantic.IMetricItem) => {
return {
label: item.name,
value: item.id,
@@ -70,7 +58,7 @@ const EntityCreateForm: ForwardRefRenderFunction<any, Props> = (
}, [metricList]);
useEffect(() => {
const dimensionEnum = dimensionList.map((item: any) => {
const dimensionEnum = dimensionList.map((item: ISemantic.IDimensionItem) => {
return {
label: item.name,
value: item.id,
@@ -128,6 +116,13 @@ const EntityCreateForm: ForwardRefRenderFunction<any, Props> = (
mode="multiple"
allowClear
style={{ width: '100%' }}
filterOption={(inputValue: string, item: any) => {
const { label } = item;
if (label.includes(inputValue)) {
return true;
}
return false;
}}
placeholder="请选择主体标识"
options={dimensionListOptions}
/>
@@ -137,6 +132,13 @@ const EntityCreateForm: ForwardRefRenderFunction<any, Props> = (
mode="multiple"
allowClear
style={{ width: '100%' }}
filterOption={(inputValue: string, item: any) => {
const { label } = item;
if (label.includes(inputValue)) {
return true;
}
return false;
}}
placeholder="请选择展示维度信息"
options={dimensionListOptions}
/>
@@ -146,6 +148,13 @@ const EntityCreateForm: ForwardRefRenderFunction<any, Props> = (
mode="multiple"
allowClear
style={{ width: '100%' }}
filterOption={(inputValue: string, item: any) => {
const { label } = item;
if (label.includes(inputValue)) {
return true;
}
return false;
}}
placeholder="请选择展示指标信息"
options={metricListOptions}
/>

View File

@@ -3,10 +3,11 @@ import React, { useState, useEffect, useRef } from 'react';
import type { Dispatch } from 'umi';
import { connect } from 'umi';
import type { StateType } from '../../model';
import { getDomainExtendConfig } from '../../service';
import { getDomainExtendConfig, getDomainExtendDetailConfig } from '../../service';
import ProCard from '@ant-design/pro-card';
import EntityCreateForm from './EntityCreateForm';
import MetricSettingForm from './MetricSettingForm';
import type { IChatConfig } from '../../data';
import DimensionMetricVisibleForm from './DimensionMetricVisibleForm';
type Props = {
@@ -17,18 +18,21 @@ type Props = {
const EntitySection: React.FC<Props> = ({ domainManger, dispatch }) => {
const { selectDomainId, dimensionList, metricList } = domainManger;
const [entityData, setEntityData] = useState<any>({});
const [entityData, setEntityData] = useState<IChatConfig.IEntity>({} as IChatConfig.IEntity);
const [themeData, setThemeData] = useState<any>({});
const entityCreateRef = useRef<any>({});
const queryThemeListData: any = async () => {
const { code, data } = await getDomainExtendConfig({
const { code, data } = await getDomainExtendDetailConfig({
domainId: selectDomainId,
});
// getDomainExtendConfig({
// domainId: selectDomainId,
// });
if (code === 200) {
const target = data?.[0] || {};
const target = data;
if (target) {
setThemeData(target);
setEntityData({
@@ -75,7 +79,14 @@ const EntitySection: React.FC<Props> = ({ domainManger, dispatch }) => {
<MetricSettingForm
domainId={Number(selectDomainId)}
themeData={themeData}
metricList={metricList}
// metricList={metricList}
metricList={metricList.filter((item) => {
const blackMetricIdList = themeData.visibility?.blackMetricIdList;
if (Array.isArray(blackMetricIdList)) {
return !blackMetricIdList.includes(item.id);
}
return false;
})}
onSubmit={() => {
queryThemeListData();
}}
@@ -86,8 +97,22 @@ const EntitySection: React.FC<Props> = ({ domainManger, dispatch }) => {
ref={entityCreateRef}
domainId={Number(selectDomainId)}
entityData={entityData}
metricList={metricList}
dimensionList={dimensionList}
// metricList={metricList}
metricList={metricList.filter((item) => {
const blackMetricIdList = themeData.visibility?.blackMetricIdList;
if (Array.isArray(blackMetricIdList)) {
return !blackMetricIdList.includes(item.id);
}
return false;
})}
// dimensionList={dimensionList}
dimensionList={dimensionList.filter((item) => {
const blackDimensionList = themeData.visibility?.blackDimIdList;
if (Array.isArray(blackDimensionList)) {
return !blackDimensionList.includes(item.id);
}
return false;
})}
onSubmit={() => {
queryThemeListData();
}}

View File

@@ -17,7 +17,7 @@ const FormItem = Form.Item;
const Option = Select.Option;
const MetricSettingForm: ForwardRefRenderFunction<any, Props> = (
{ metricList, domainId, themeData: uniqueMetricData },
{ metricList, domainId, themeData: uniqueMetricData, onSubmit },
ref,
) => {
const [form] = Form.useForm();
@@ -82,6 +82,7 @@ const MetricSettingForm: ForwardRefRenderFunction<any, Props> = (
if (code === 200) {
form.setFieldValue('id', data);
onSubmit?.();
message.success('保存成功');
return;
}
@@ -116,6 +117,13 @@ const MetricSettingForm: ForwardRefRenderFunction<any, Props> = (
allowClear
showSearch
style={{ width: '100%' }}
filterOption={(inputValue: string, item: any) => {
const { label } = item;
if (label.includes(inputValue)) {
return true;
}
return false;
}}
placeholder="请选择展示指标信息"
options={metricListOptions}
/>

View File

@@ -0,0 +1,28 @@
import { IChatConfig, ISemantic } from '../../data';
export const exChangeRichEntityListToIds = (entityData: IChatConfig.IEntity) => {
const entityList = entityData.entityIds || [];
const detailData: {
dimensionIds: number[];
metricIds: number[];
} = { dimensionIds: [], metricIds: [] };
const { dimensionList, metricList } = entityData.entityInternalDetailDesc || {};
if (Array.isArray(dimensionList)) {
detailData.dimensionIds = dimensionList.map((item: ISemantic.IDimensionItem) => {
return item.id;
});
}
if (Array.isArray(metricList)) {
detailData.metricIds = metricList.map((item: ISemantic.IMetricItem) => {
return item.id;
});
}
const entityIds = entityList.map((item) => {
return item.id;
});
return {
...entityData,
entityIds,
detailData,
};
};

View File

@@ -159,6 +159,9 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
>
<Input placeholder="名称不可重复" disabled={isEdit} />
</FormItem>
<FormItem name="alias" label="别名">
<Input placeholder="多个别名用英文逗号隔开" />
</FormItem>
<FormItem
name="sensitiveLevel"
label="敏感度"

View File

@@ -215,7 +215,7 @@ const PermissionTable: React.FC<Props> = ({ domainManger }) => {
okText="是"
cancelText="否"
onConfirm={async () => {
const { code } = await removeGroupAuth({
const { code, msg } = await removeGroupAuth({
domainId: record.domainId,
groupId: record.groupId,
});
@@ -223,7 +223,7 @@ const PermissionTable: React.FC<Props> = ({ domainManger }) => {
setPermissonData({});
queryListData();
} else {
message.error('删除失败');
message.error(msg);
}
}}
>

View File

@@ -1,7 +1,7 @@
import { DownOutlined, PlusOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons';
import { Input, message, Tree, Popconfirm, Space, Tooltip } from 'antd';
import { Input, message, Tree, Popconfirm, Space, Tooltip, Row, Col } from 'antd';
import type { DataNode } from 'antd/lib/tree';
import React, { useEffect, useState } from 'react';
import { useEffect, useState } from 'react';
import type { FC, Key } from 'react';
import { connect } from 'umi';
import type { Dispatch } from 'umi';
@@ -21,6 +21,8 @@ type ProjectListProps = {
selectDomainName: string;
createDomainBtnVisible?: boolean;
dispatch: Dispatch;
onCreateDomainBtnClick?: () => void;
onTreeSelected?: () => void;
};
const projectTreeFlat = (projectTree: DataNode[], filterValue: string): DataNode[] => {
@@ -41,6 +43,8 @@ const projectTreeFlat = (projectTree: DataNode[], filterValue: string): DataNode
const ProjectListTree: FC<ProjectListProps> = ({
selectDomainId,
createDomainBtnVisible = true,
onCreateDomainBtnClick,
onTreeSelected,
dispatch,
}) => {
const [projectTree, setProjectTree] = useState<DataNode[]>([]);
@@ -89,6 +93,7 @@ const ProjectListTree: FC<ProjectListProps> = ({
const targetNodeData = classList.filter((item: any) => {
return item.id === selectedKeys;
})[0];
onTreeSelected?.();
dispatch({
type: 'domainManger/setSelectDomain',
selectDomainId: selectedKeys,
@@ -196,28 +201,29 @@ const ProjectListTree: FC<ProjectListProps> = ({
return (
<div className={styles.projectList}>
<h2 className={styles.treeTitle}>
<span className={styles.title}></span>
<Space>
{createDomainBtnVisible && (
<Tooltip title="新增顶级域">
<PlusCircleOutlined
onClick={() => {
setProjectInfoParams({ type: 'top', modelType: 'add' });
setProjectInfoModalVisible(true);
}}
className={styles.addBtn}
/>
</Tooltip>
)}
</Space>
</h2>
<Search
allowClear
className={styles.search}
placeholder="请输入主题域名称进行查询"
onSearch={onSearch}
/>
<Row>
<Col flex="1 1 200px">
<Search
allowClear
className={styles.search}
placeholder="请输入主题域名称进行查询"
onSearch={onSearch}
/>
</Col>
<Col flex="0 1 50px">
<Tooltip title="新增顶级域">
<PlusCircleOutlined
onClick={() => {
setProjectInfoParams({ type: 'top', modelType: 'add' });
setProjectInfoModalVisible(true);
onCreateDomainBtnClick?.();
}}
className={styles.addBtn}
/>
</Tooltip>
</Col>
</Row>
<Tree
expandedKeys={expandedKeys}
onExpand={handleExpand}

View File

@@ -3,62 +3,7 @@
flex-direction: row;
background-color: #fff;
height: calc(100vh - 48px);
.projectList {
display: flex;
flex-direction: column;
// width: 400px;
overflow: hidden;
// min-height: calc(100vh - 48px);
border-right: 1px solid #d9d9d9;
.treeTitle {
margin-bottom: 0;
padding: 20px;
line-height: 34px;
text-align: right;
background: #fff;
border-bottom: 1px solid #d9d9d9;
.title {
float: left;
}
.addBtn {
cursor: pointer;
&:hover {
color: #296DF3;
}
}
}
.search {
width: calc(100% - 20px);
margin: 10px;
}
.tree {
flex: 1;
padding: 10px;
.projectItem {
display: flex;
width: 100%;
cursor: auto;
.title {
flex: 1;
cursor: pointer;
}
.operation {
.icon {
margin-left: 6px;
cursor: pointer;
}
}
}
}
}
.projectManger {
width: 100%;
@@ -139,6 +84,65 @@
}
}
.projectList {
display: flex;
flex-direction: column;
width: 400px;
overflow: hidden;
// min-height: calc(100vh - 48px);
.addBtn {
cursor: pointer;
width: 100%;
font-size: 18px;
margin-top: 18px;
&:hover {
color: #296DF3;
}
}
.treeTitle {
margin-bottom: 0;
padding: 20px;
line-height: 34px;
text-align: right;
background: #fff;
border-bottom: 1px solid #d9d9d9;
.title {
float: left;
}
}
.search {
width: calc(100% - 20px);
margin: 10px;
}
.tree {
flex: 1;
padding: 10px;
.projectItem {
display: flex;
width: 100%;
cursor: auto;
.title {
flex: 1;
cursor: pointer;
}
.operation {
.icon {
margin-left: 6px;
cursor: pointer;
}
}
}
}
}
.user {
display: grid;
}
@@ -221,4 +225,25 @@
background: #f8f9fb;
}
}
}
}
.domainSelector {
display: flex;
width: max-content;
padding: 0 11px;
cursor: pointer;
position: relative;
// background-color: #fff;
// border: 1px solid #d9d9d9;
border-radius: 4px;
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
color: #000a24d9;
.downIcon {
margin-left: 10px;
font-size: 14px;
}
&:hover {
color:#296DF3;
}
}