add code editor in doc page

This commit is contained in:
silenceqi 2020-12-28 12:47:34 +08:00
parent a74511ad4d
commit 46bee258e2
4 changed files with 310 additions and 26 deletions

View File

@ -17,6 +17,7 @@ type docReqBody struct {
PageSize int `json:"pageSize"`
Filter string `json:"filter"`
Cluster string `json:"cluster"`
Keyword string `json:"keyword"`
}
func (handler APIHandler) HandleDocumentAction(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
@ -93,10 +94,14 @@ func (handler APIHandler) HandleDocumentAction(w http.ResponseWriter, req *http.
}
from := (pageIndex - 1) * pageSize
filter := `{"match_all": {}}`
if reqBody.Keyword != "" {
filter = fmt.Sprintf(`{"query_string":{"query":"%s"}}`, reqBody.Keyword)
}
if reqBody.Filter != "" {
filter = reqBody.Filter
}
query := fmt.Sprintf(`{"from":%d, "size": %d, "query": %s}`, from, pageSize, filter)
fmt.Println(query)
var reqBytes = []byte(query)
resp, err := client.SearchWithRawQueryDSL(indexName, reqBytes)
if err != nil {

View File

@ -8,6 +8,7 @@
"@antv/data-set": "^0.9.6",
"@antv/g2-brush": "^0.0.2",
"@babel/runtime": "^7.1.2",
"@monaco-editor/react": "^3.7.4",
"@svgdotjs/svg.js": "^3.0.16",
"antd": "^3.26.18",
"antd-table-infinity": "^1.1.6",

View File

@ -1,7 +1,84 @@
import React, { Component } from 'react';
import React, { Component, createRef } from 'react';
import router from 'umi/router';
import { connect } from 'dva';
import { Col, Form, Row,Select, Input, Card,Icon, Table, InputNumber, Popconfirm,
Divider,Button,Tooltip, Cascader } from 'antd';
Divider,Button,Tooltip, Cascader, Modal } from 'antd';
import Editor, {monaco} from '@monaco-editor/react';
import {createDependencyProposals} from './autocomplete';
function findParentIdentifier(textUntilPosition){
let chars = textUntilPosition;
let length = chars.length;
let stack = [];
let targetIdx = -1;
for(let i = length-1; i>-1; i--){
if(chars[i] == '}'){
stack.push('}');
}else if(chars[i] == '{'){
if(stack.length == 0){
targetIdx = i;
break;
}
stack.pop();
}
}
let foundColon = false;
for(let i = targetIdx; i > -1; i--){
if(chars[i] == ":"){
targetIdx = i;
foundColon = true;
break;
}
}
if(!foundColon){
return ""
}
let identifer = [];
let startFound = false;
for(let i = targetIdx; i > -1; i--){
if((chars[i]>='a' && chars[i] <= 'z') || chars[i] == '_'){
identifer.push(chars[i]);
startFound = true;
}else if(startFound){
break;
}
}
return identifer.reverse().join('');
}
monaco.init().then((mi)=>{
mi.languages.registerCompletionItemProvider('json', {
provideCompletionItems: function(model, position) {
// find out if we are completing a property in the 'dependencies' object.
var textUntilPosition = model.getValueInRange({startLineNumber: 1, startColumn: 1, endLineNumber: position.lineNumber, endColumn: position.column});
if(textUntilPosition.indexOf('{') < 0){
return { suggestions: [] };
}
let key = findParentIdentifier(textUntilPosition);
//console.log(key);
// var match = textUntilPosition.match(/"match"\s*:\s*\{\s*("[^"]*"\s*:\s*"[^"]*"\s*,\s*)*([^"]*)?$/);
// if (!match) {
// return { suggestions: [] };
// }
var word = model.getWordUntilPosition(position);
var range = {
startLineNumber: position.lineNumber,
endLineNumber: position.lineNumber,
startColumn: word.startColumn,
endColumn: word.endColumn
};
return {
suggestions: createDependencyProposals(key, range, mi)
};
}
});
})
const {Option} = Select;
const EditableContext = React.createContext();
@ -130,7 +207,7 @@ class EditableCell extends React.Component {
dispatch({
type: 'document/addDocItem',
payload: {
index: doclist.index,
index: doclist._index,
data: row,
}
})
@ -244,22 +321,37 @@ class EditableCell extends React.Component {
@Form.create()
class Doucment extends React.Component {
state={
bodyDisplay: 'none',
bodyDisplay: 'none',
}
// constructor(props){
// super(props);
// this.filterGetter = createRef();
// }
fetchData = (params) => {
const {dispatch} = this.props;
dispatch({
return dispatch({
type: 'document/fetchDocList',
payload: params,
})
}
handleEditorDidMount = (_valueGetter) =>{
this.filterGetter = _valueGetter;
}
componentDidMount(){
const {location } = this.props;
//console.log(match, location);
let index = location.query.index || 'infini-test';
let cluster = location.query.cluster || 'single-es';
this.indexEl.setState({
value: [cluster, index]
})
this.fetchData({
pageSize: 10,
pageIndex: 1,
index: 'infini-test',
index,
})
}
@ -268,6 +360,21 @@ class Doucment extends React.Component {
if(!document.data || document.data.length == 0 || document.isAddNew){
return;
}
let {indices} = document;
let _index = indices[0];
if(indices && indices.length > 1){
console.log(this.indexSelEl);
let vals = this.indexSelEl.rcSelect.state.value;
if(vals.length == 0){
Modal.error({
title: '系统提示',
content: '请选择新建文档目标索引',
});
return
}else{
_index = vals[0];
}
}
let keys = Object.keys(document.data[0])
let newDoc = {};
for(let key of keys){
@ -278,36 +385,45 @@ class Doucment extends React.Component {
payload: {
docItem: newDoc,
extra: {
isAddNew: true
isAddNew: true,
_index
}
},
})
}
handleSearchClick = (value)=>{
const [cluster, index] = this.indexEl.state.value;
let targetIndex = index;
if(value != ""){
targetIndex = value;
let [cluster, index] = this.indexEl.state.value;
let rewriteIndex = this.reindexEl.state.value;
if(typeof rewriteIndex !== 'undefined' && rewriteIndex !=""){
index = rewriteIndex;
}
console.log(targetIndex);
this.fetchData({
cluster,
index: targetIndex,
index: index,
pageSize: 10,
pageIndex: 1,
filter: this.filterEl.state.value,
//filter: this.filterEl.state.value,
filter: this.filterGetter(),
keyword: value,
}).then(()=>{
if(this.hashChanged){
router.push(`/data/doc?index=${index}`);
this.hashChanged = !this.hashChanged;
}
})
}
renderNew = ()=>{
const {indices} = this.props.document;
if((indices && indices.length > 1)){
return;
}
// if((indices && indices.length > 1)){
// return;
// }
return (
<div>
{(indices && indices.length > 1) ? (<Select style={{width: 200, marginRight:5}} placeholder="please select a index">
{(indices && indices.length > 1) ? (<Select ref={el=>{this.indexSelEl=el}} style={{width: 200, marginRight:5}} placeholder="please select a index">
{indices.map(item=>{
return (<Select.Option key={item} label={item}>{item}</Select.Option>)
})}
@ -328,25 +444,30 @@ class Doucment extends React.Component {
{
value: 'infini-test',
label: 'infini-test',
},
{
value: 'infini-test1',
label: 'infini-test1',
}
]}];
return (
<div>
<Card>
<Row gutter={[16, { xs: 8, sm: 16, md: 24, lg: 32 }]}>
<Col span={20}>
<Col span={20} style={{paddingLeft:0}}>
<Input.Group compact>
<Cascader
options={options}
ref={el=>{this.indexEl=el}}
style={{width: '20%'}}
onChange={(value, selectedOptions)=>{console.log(value)}}
onChange={(values, selectedOptions)=>{this.reindexEl.setState({value: values[1]}); this.hashChanged = true;}}
placeholder="Please select index"
showSearch={{filter: (inputValue, path)=>path.some(option => option.label.toLowerCase().indexOf(inputValue.toLowerCase()) > -1) }}
/>
<Input ref={el=>{this.reindexEl=el}} placeholder="rewrite index or index pattern" style={{width: '20%'}}/>
<Input.Search
style={{width:"80%"}}
placeholder="input rewrite index or index pattern"
style={{width:"50%"}}
placeholder="input search keyword"
enterButton="execute"
onSearch={this.handleSearchClick}
/>
@ -369,8 +490,22 @@ class Doucment extends React.Component {
</Col>
</Row>
<Row style={{display: this.state.bodyDisplay}} gutter={[16, { xs: 8, sm: 16, md: 24, lg: 32 }]}>
<Col span={20}>
<Input.TextArea ref={el=>{this.filterEl=el}} placeholder="input query filter (elasticsearch query DSL)" rows={8}/>
<Col span={20} style={{border:'1px solid #e8e8e8'}}>
{/* <Input.TextArea ref={el=>{this.filterEl=el}} placeholder="input query filter (elasticsearch query DSL)" rows={8}/> */}
<Editor
height="200px"
language="json"
theme="light"
// value={`{"match":{"name": "cincky"}}`}
options={{
minimap: {
enabled: false,
},
tabSize: 2,
wordBasedSuggestions: true,
}}
editorDidMount={this.handleEditorDidMount}
/>
</Col>
</Row>
</Card>

View File

@ -0,0 +1,143 @@
function registerKeyProposals(key){
let proposals = {
'bool': [{
label: 'filter',
documentation: "filter",
insertText: '"filter": [\n\t{}\n]',
},{
label: 'minimum_should_match',
insertText: '"minimum_should_match": 1',
},{
label: 'must',
insertText: '"must": [\n\t{}\n]',
},{
label: 'must_not',
insertText: '"must_not": [\n\t{}\n]',
},{
label: 'should',
insertText: '"should": [\n\t{}\n]',
}],
'multi_match':[{
label: 'analyzer',
insertText: '"analyzer": ""',
},{
label: 'boost',
insertText: '"boost": 1',
},{
label: 'fuzziness',
insertText: '"fuzziness": 1',
},{
label: 'minimum_should_match',
insertText: '"minimum_should_match": 1',
},{
label: 'operator',
insertText: '"operator": "and"',
},{
label: 'type',
insertText: '"type": "best_fields"',
}]
};
if(proposals[key]){
return proposals[key];
}else{
return [];
}
}
export function createDependencyProposals(key, range, mi) {
if(['', 'must', 'must_not', 'should'].includes(key)){
return queryProposals.map(p=>{
return {
kind: mi.languages.CompletionItemKind.Property,
...p,
insertTextRules: mi.languages.CompletionItemInsertTextRule.InsertAsSnippet,
range: range
}
})
}
return registerKeyProposals(key).map(p=>{
return {
kind: mi.languages.CompletionItemKind.Property,
...p,
insertTextRules: mi.languages.CompletionItemInsertTextRule.InsertAsSnippet,
range: range
}
});
}
let queryProposals = [{
label: 'bool',
documentation: "bool query",
insertText: '"bool": {}',
},{
label: 'exists',
documentation: "exists query",
insertText: '"exists": {}',
},{
label: 'fuzzy',
documentation: "fuzzy query",
insertText: '"fuzzy": {\n\t"${1:FIELD}": {}\n}',
insertTextRules: 1,
},{
label: 'match',
documentation: "match query",
insertText: '"match": {\n\t"${1:FIELD}": "${2:TEXT}"\n}',
insertTextRules: 1,
},{
label: 'match_all',
documentation: "match_all query",
insertText: '"match_all": {}',
},{
label: 'match_phrase',
documentation: "match_phrease query",
insertText: '"match_phrase": {\n\t"${1:FIELD}": "${2:PHRASE}"\n}',
insertTextRules: 1,
},{
label: 'match_phrase_prefix',
documentation: "match_phrease query",
insertText: '"match_phrase_prefix": {\n\t"${1:FIELD}": "${2:PREFIX}"\n}',
insertTextRules: 1,
},{
label: 'multi_match',
documentation: "multi_match query",
insertText: '"multi_match": {\n\t"query": "${1:}",\n\t"fields": [${2:}]\n}',
insertTextRules: 1,
},{
label: 'nested',
documentation: "nested query",
insertText: '"nested": {\n\t"path": "${1:path_to_nested_doc}",\n\t"query": {}\n}',
insertTextRules: 1,
},{
label: 'prefix',
documentation: "prefix query",
insertText: '"prefix": {\n\t"${1:FIELD}": {\n\t\t"value": "${2:}"\n\t}\n}',
insertTextRules: 1,
},{
label: 'query_string',
documentation: "query_string query",
insertText: '"query_string": {\n\t"default_field": "${1:FIELD}",\n\t"query": "${2:this AND that OR thus}"\n}',
insertTextRules: 1,
},{
label: 'range',
documentation: "range query",
insertText: '"range": {\n\t"${1:FIELD}":{\n\t\t"gte": 0,\n\t\t"lte": 10\n\t}\n}',
insertTextRules: 1,
},{
label: 'regexp',
documentation: "regexp query",
insertText: '"regexp": {\n\t"${1:FIELD}": "${2:REGEXP}"\n}',
insertTextRules: 1,
},{
label: 'term',
documentation: "term query",
insertText: '"term": {\n\t"${1:FIELD}": {\n\t\t"value": "${2:}"\n\t}\n}',
insertTextRules: 1,
},{
label: 'terms',
documentation: "terms query",
insertText: '"terms": {\n\t"${1:FIELD}": [\n\t\t"VALUE1",\n\t\t"VALUE2"\n\t]\n}',
insertTextRules: 1,
},]