bill 5 лет назад
Родитель
Сommit
7b1956ea77

+ 2 - 0
package.json

@@ -8,6 +8,7 @@
     "@types/node": "12.12.9",
     "@types/ol": "^5.3.6",
     "@types/react": "16.9.11",
+    "@types/react-color": "^3.0.1",
     "@types/react-dom": "16.9.4",
     "@types/react-router": "^5.1.3",
     "@types/react-router-dom": "^5.1.3",
@@ -18,6 +19,7 @@
     "ol": "4.3.3",
     "react": "^16.12.0",
     "react-app-rewired": "^2.1.5",
+    "react-color": "^2.17.3",
     "react-dom": "^16.12.0",
     "react-router": "^5.1.2",
     "react-router-dom": "^5.1.2",

+ 7 - 0
public/style.css

@@ -58,6 +58,7 @@ html, body, #root, .app {
   flex: 0 0 260px;
 }
 
+select,
 input {
   margin: 0 8px;
   background: transparent;
@@ -65,4 +66,10 @@ input {
   border: 1px solid #2e2e2e;
   outline: none;
   color: inherit;
+  border-radius: 3px;
+}
+
+.cesium-viewer-fullscreenContainer,
+.cesium-viewer-bottom {
+  display: none !important;
 }

+ 43 - 0
src/components/Color/index.tsx

@@ -0,0 +1,43 @@
+import React, {useState, Fragment} from 'react'
+import { SketchPicker } from 'react-color'
+import Dialog from '../Dialog'
+import styles from './style.module.css'
+
+interface Props {
+  color: string,
+  onChange: {
+    (color: string): void
+  }
+}
+
+
+function Color(props: Props) {
+  let [show, setShow] = useState(false)
+  let [color, setColor] = useState(props.color)
+
+  return (
+    <Fragment>
+      <span
+        style={{ backgroundColor: props.color }}
+        className={styles.showcolor}
+        onClick={ev =>setShow(true)}>
+      </span>
+
+      <Dialog 
+        title="选择颜色"
+        show={show}
+        stateChange={(save: boolean) => {
+          setShow(false);
+          save && props.onChange(color)
+      }}>
+        <div className={styles.splayer} style={{textAlign: 'center'}}>
+          <SketchPicker 
+            color={color}
+            onChange={s => setColor(`rgba(${s.rgb.r},${s.rgb.g},${s.rgb.b},${s.rgb.a})`)} />
+        </div>
+      </Dialog>
+    </Fragment>
+  )
+}
+
+export default Color

+ 20 - 0
src/components/Color/style.module.css

@@ -0,0 +1,20 @@
+.showcolor {
+  width: 44px;
+  height: 15px;
+  padding: 4px 10px;
+  display: inline-block;
+  border: 1px solid #2e2e2e;
+  vertical-align: text-top;
+}
+
+.splayer {
+  text-align: center;
+}
+
+.splayer > div {
+  display: inline-block;
+}
+
+.splayer input {
+  color: rgb(34, 34, 34);
+}

+ 6 - 2
src/components/Dialog/index.tsx

@@ -5,7 +5,7 @@ import styles from './style.module.css'
 
 interface Props {
   node: HTMLDivElement,
-  children: Array<any> | Function,
+  children: any,
   stateChange?: Function,
   show?: boolean,
   title?: string
@@ -42,4 +42,8 @@ function Dialog({node, children, stateChange, show = false, title}: Props) {
   return ReactDOM.createPortal(Layer, node)
 }
 
-export default Dialog
+
+const node = document.querySelector('#dialog')
+const GDialog = (props: any) => <Dialog node={node} {...props} />
+
+export default GDialog

+ 1 - 1
src/components/Dialog/style.module.css

@@ -5,7 +5,7 @@
   bottom: 0;
   right: 0;
   z-index: 99;
-  background-color: rgba(0,0,0,0.3);
+  background-color: rgba(0,0,0,0.5);
   display: flex;
   align-items: center;
   justify-content: center;

+ 2 - 1
src/components/Upload/file.d.ts

@@ -7,5 +7,6 @@ interface UFile {
 
 interface UFileC extends UFile {
   percentage: number,
-  status: number
+  status: number,
+  closeHandle?: Function
 }

+ 28 - 6
src/components/Upload/index.tsx

@@ -57,13 +57,13 @@ interface ItemCtrlProps extends UFile {
   handleUpSuccess: Function,
   multiple?: boolean,
   body: Object,
-  check?: string
+  check?: string,
+  closeHandle: Function
 }
 
 function ItemCtrl(props: ItemCtrlProps) : ReactElement {
   const [status, setStatus] = useState(UPREADY)
   const [percentage, setPercentage] = useState(status === UPREADY ? 0 : 100)
-
   if (status === UPREADY) {
     setStatus(UPING)
     uploadFile({
@@ -83,7 +83,7 @@ function ItemCtrl(props: ItemCtrlProps) : ReactElement {
     })
   }
   
-  return <Item {...props} status={status} percentage={percentage} />
+  return <Item {...props} status={status} percentage={percentage} closeHandle={props.closeHandle} />
 }
 
 interface UploadProps {
@@ -92,7 +92,8 @@ interface UploadProps {
   api: string,
   upHandle: Function,
   body?: Object | null,
-  check?: string
+  check?: string,
+  filter?: Function
 }
 
 function Upload(props: UploadProps) : ReactElement {
@@ -101,15 +102,27 @@ function Upload(props: UploadProps) : ReactElement {
   const onClickHandle = () => inputfile.current.click()
   const onChangeHandle = async () => {
     let files = [...inputfile.current.files]
+    if (files.length === 0) return;
     let newFiles = await Promise.all(
       files.map(async (file: File) => ({
         name: file.name,
         size: file.size,
         file: file
       }))
-    )
+    );
+
+    if (props.filter) {
+      let ret = props.filter(props.multiple ? files : files[0])
+      if (!ret.accord) {
+        inputfile.current.value = null
+        alert(ret.msg)
+        return false;
+      }
+    }
+
     for (let i = 0; i < newFiles.length; i++) {
       if (escape(newFiles[i].name).indexOf("%u") >= 0) {
+        inputfile.current.value = null
         alert("上传文件文件名不能包含中文!");
         return false;
       }
@@ -119,7 +132,15 @@ function Upload(props: UploadProps) : ReactElement {
       size: newFiles.reduce((previous, current) => previous + current.size, 0),
       file: newFiles.map(f => f.file)
     }
-    setFlist(flist.concat(newFile as any))
+    setFlist([])
+    setTimeout(() => setFlist([newFile]), 500)
+    inputfile.current.value = null
+  }
+
+  const closeHandle = (file:any) => {
+    let fl = [...flist]
+    fl.splice(fl.indexOf(file), 1)
+    setFlist(fl)
   }
 
   return (
@@ -131,6 +152,7 @@ function Upload(props: UploadProps) : ReactElement {
       <List data={flist} className="uplist">
         {(file: UFile, i: number, s: string) => (
           <ItemCtrl
+            closeHandle={() => closeHandle(file) }
             handleUpSuccess={props.upHandle}
             body={props.body ? props.body: {}}
             api={props.api} {...file}

+ 40 - 0
src/http.ts

@@ -1,5 +1,45 @@
 import axios from 'axios'
+import {history} from './router.config'
+
+const replaceLogin = () => history.replace('/login')
+let token = sessionStorage.getItem('token')
+token || replaceLogin()
+
 
 axios.defaults.baseURL = '/api/'
+axios.interceptors.request.use(request => {
+  if (request.url === '/login' || request.url === '/logout') {
+    request.baseURL = process.env.NODE_ENV === 'development' ? '/test': ''
+  }
+
+  if (!token && request.url !== '/login') {
+    replaceLogin()
+    return Promise.reject('1')
+  }
+
+  request.headers.token = token
+  return request
+})
+
+axios.interceptors.response.use(response => {
+  if (response.data.code === 401) {
+    sessionStorage.removeItem('token')
+    token = null;
+    replaceLogin()
+  }
+
+  return response
+})
+
+export const setToken = (t: any) => {
+  token = t
+  sessionStorage.setItem('token', t)
+}
+
+
+export const removeToken = (t: any) => {
+  token = null
+  sessionStorage.removeItem('token')
+}
 
 export default axios

+ 18 - 16
src/index.tsx

@@ -2,14 +2,12 @@ import React, {useState} from 'react';
 import ReactDOM from 'react-dom';
 import * as serviceWorker from './serviceWorker';
 import {Route, Router} from 'react-router'
-import { createHashHistory } from 'history'
-import config from './router.config'
+import config, {history} from './router.config'
 import Header from './layout/Header'
 import Slide from './layout/Slide';
 import Combination from './layout/Combination'
 import { RouteComponentProps, Switch, Redirect } from 'react-router'
-
-const history = createHashHistory()
+import Login from './page/Login'
 
 
 function App() {
@@ -31,21 +29,25 @@ function App() {
   ))
 
   return (
-    <div className="app">
-      <Header className='header' />
-      <div className='section'>
-        <Router history={history}>
-          <Route path="/" component={() => <Slide {...route} className='slide' />} />
-          <Switch>
-            {Items}
-            <Redirect to="/gis" />
-          </Switch>
-        </Router>
-      </div>
-    </div>
+    <Router history={history}>
+      <Switch>
+        <Route path="/login" component={Login} />
+        <div className="app">
+          <Header className='header' />
+          <div className='section'>
+            <Route path="/" component={() => <Slide {...route} className='slide' />} />
+            <Switch>
+              {Items}
+              <Redirect to="/gis" />
+            </Switch>
+          </div>
+        </div>
+      </Switch>
+    </Router>
   )
 }
 
+
 ReactDOM.render(<App />, document.getElementById('root'));
 
 // If you want your app to work offline and load faster, you can change

+ 1 - 1
src/layout/Header.tsx

@@ -13,7 +13,7 @@ const style = {
 function Header(props: Props) {
   return (
     <div style={style.title} className={props.className}>
-      <h1>文件管理系统</h1>
+      <h1>GIS数据管理</h1>
     </div>
   )
 }

+ 1 - 1
src/page/List/Coor.tsx

@@ -47,7 +47,7 @@ export default function Coor(props: {coor: string, onChange: Function}) {
 
   return (
     <div className={styles.tip + ' ' + styles.inputitem}>
-      输入坐标:<input value={coor} placeholder="输入对应坐标" onChange={changeInput} onFocus={changeInput} onBlur={blurHandle} />
+      输入坐标转换参数:<input value={coor} placeholder="输入对应坐标" onChange={changeInput} onFocus={changeInput} onBlur={blurHandle} />
       <div style={{ display: info ? 'block' : 'none' }}>
         <p>{info}</p>
         <p>以,(英文符号)结束输入,并输入下一个参数如0,0,0</p>

+ 62 - 4
src/page/List/GeoList.tsx

@@ -3,10 +3,17 @@ import GrentReducer from './grent'
 import Upload from '../../components/Upload'
 import { Link } from 'react-router-dom'
 import styles from './index.module.css'
+import { sectionStepAction } from './ListState'
+import Step from '../../components/Upload/Step'
 import Coor from './Coor'
+import path from 'path'
+
+
+const intervals: Array<any> = []
 
 export default function GeoList({ className }: any) {
-  const { referData, Element, models } = GrentReducer({
+  intervals.forEach(interval => clearInterval(interval))
+  const { referData, Element, models, referItem, modelDispatch } = GrentReducer({
     delUrl: '/vector/delete/',
     getUrl: '/vector/list/',
     zipUrl: '/vector/unzip/',
@@ -15,7 +22,10 @@ export default function GeoList({ className }: any) {
     transformUrl: '/vector/command/geojson/',
     judgeUrl: '/vector/command/judge/coord/',
     ItemFn (model: Model) {
-      if (model.status === 8 || model.status === 11 || model.status === 12) {
+      console.log(model.status)
+      if (model.status === 6) {
+        return <Step step={model.sectStep ? model.sectStep : 0} />
+      } else if (model.status === 8 || model.status === 11 || model.status === 12) {
         return <Link to={"/style/" + model.id} style={{ color: '#3e7cd3' }}>编辑样式</Link>
       }
     },
@@ -34,14 +44,62 @@ export default function GeoList({ className }: any) {
 
   let coord = coorstr.split(',').length > 1 ? coorstr.split(',') : []
 
+  async function getStep(model: Model) {
+    let data = await sectionStepAction(modelDispatch, `/vector/progress/${model.id}/`, model)
+    return data.data && data.data.progress
+  };
+
+  models.forEach((model: any) => {
+    if (model.status !== 6) return;
+
+    let interval = setInterval(async () => {
+      let step = await getStep(model)
+      if (model.sectStep !== step) {
+        model.sectStep = step
+        model = referItem(model)
+        clearInterval(interval)
+
+        if (model.sectStep === 100) {
+          setTimeout(() => referData(), 1000)
+        }
+      }
+    }, 1000)
+    intervals.push(interval)
+  })
+
+  function uploadFilter(files: Array<File>) {
+    let susts = ['.shp', '.prj', '.dbf', '.shx']
+    let optional = ['.sbx', '.sbn', '.xml']
+    let exts = files.map(file => path.extname(file.name))
+    let lack = susts.filter(sust => !exts.some(ext => ext === sust))
+    let ret: {msg?: string, accord: boolean} = { accord: true }
+
+    if (lack.length) {
+      ret.msg = `矢量数据必须包含${lack.join(',')}文件`
+      ret.accord = false
+      return ret
+    }
+
+    let all = susts.concat(optional)
+    let surplus = exts.filter(ext => !all.some(e => e === ext))
+    if (surplus.length) {
+      ret.msg = `矢量数据不需要${surplus.join(',')}文件`
+      ret.accord = false
+      return ret
+    }
+
+    return ret
+  }
+
   return (
     <div className={className}>
       <div className={styles.uplayer + ' ' + styles.inuplayer} >
         <Coor coor={coorstr} onChange={(val:string) => setCoor(val)} />
         <div className={styles.inputitem}>
-          输入目录:<input value={dir} placeholder="输入对应文件夹" onChange={ev => setDir(ev.target.value)} />
+          填写目录:<input value={dir} placeholder="输入对应目录" onChange={ev => setDir(ev.target.value)} />
         </div>
-        <Upload api={'/vector/uploadMult/' + dir + '/'} body={{ coord }} upHandle={referData} multiple className={styles.inputitem} />
+        {dir && <Upload api={'/vector/uploadMult/' + dir + '/'} body={{ coord }} upHandle={referData} multiple className={styles.inputitem} filter={uploadFilter} />}
+        
       </div>
       {Element}
     </div>

+ 78 - 55
src/page/List/GrentOper.tsx

@@ -1,7 +1,7 @@
-import React, { Fragment, useState } from 'react'
+import React, { Fragment } from 'react'
 import { zipItemAction, sectionItemAction, judgeItemAction, transferItemAction } from './ListState'
 import Item from '../../components/item'
-import GDialog from '../../components/Dialog'
+import Dialog from '../../components/Dialog'
 import styles from './index.module.css'
 
 const JUDGEING = 1, JUGESUCCESS = 2, JUGEERR = 3
@@ -9,11 +9,9 @@ const SECTIONING = 4, SECTIONSUCCESS = 5, SECTIONEERR = 6
 const TRANING = 7, TRANSUCCESS = 8, TRANEERR = 9
 const ZIPING = 10, ZIPSUCCESS = 11, ZIPEERR = 12
 const TRANSFERING = 13, TRANSFERSUCCESS = 14, TRANSFEREERR = 15
-const node = document.querySelector('#dialog')
-const Dialog = (props: any) => <GDialog node={node} {...props} />
 
 
-export default function Grent({ setItemStaus, modelDispatch, referData, delHandle, api, region, showDialog, setShowDialog }: any) {
+export default function Grent({ setItemStaus, modelDispatch, referData, delHandle, api, region }: any) {
   let [text, setText] = ['', (dom: HTMLInputElement) => { text = dom.value }]
   let [min, setMin] = ['9', (dom: HTMLInputElement) => {
     let i = Number(dom.value)
@@ -24,7 +22,7 @@ export default function Grent({ setItemStaus, modelDispatch, referData, delHandl
       min = dom.value
     }
   }]
-  let [max, setMax] = ['20', (dom: HTMLInputElement) => {
+  let [max, setMax] = ['17', (dom: HTMLInputElement) => {
     let i = Number(dom.value)
     let imi = Number(min)
     if (isNaN(i) || (imi && i < imi) || i < 9 || i > 20) {
@@ -101,64 +99,89 @@ export default function Grent({ setItemStaus, modelDispatch, referData, delHandl
       });
   }
 
-  const ItemFn = (model: Model, privItem: Function | void) => {
-    let Zip: any = <b onClick={() => zipHandle(model)} style={{ cursor: 'pointer', color: '#3e7cd3' }}>解压</b>
-    let Sect: any = <b onClick={() => section(model)} style={{ cursor: 'pointer', color: '#3e7cd3' }}>切片</b>
-    let Jude: any = <b onClick={() => judge(model)} style={{ cursor: 'pointer', color: '#3e7cd3' }}>判断</b>
-    let Tran: any = <b onClick={() => transform(model)} style={{ cursor: 'pointer', color: '#3e7cd3' }}>转geojson</b>
-    let Tf: any = (
-      <Fragment>
-        <Dialog title="服务发布" show={showDialog} stateChange={(enter: boolean) => { setShowDialog(false); enter && transfer(model) }}>
-          <div className={styles.dialogitem}>
-            <label>发布参数</label>
-            <input type="text" defaultValue={text} onChange={ev => setText(ev.target)} placeholder="发布参数" />
-          </div>
-        </Dialog>
-        <b style={{ cursor: 'pointer', color: '#3e7cd3' }} onClick={() => setShowDialog(true)}>发布</b>
-      </Fragment>
-    )
-
-    if (region) {
-      Sect = (
+  class ItemFn extends React.Component<any, any> {
+    constructor(props: any) {
+      super(props)
+      this.state = {
+        showDialog: false
+      }
+    }
+    setShowDialog = (b: any) => {
+      this.setState({showDialog: b})
+    }
+    render() {
+      let {model, privItem} = this.props
+      let Zip: any = <b onClick={() => zipHandle(model)} style={{ cursor: 'pointer', color: '#3e7cd3' }}>解压</b>
+      let Sect: any = <b onClick={() => section(model)} style={{ cursor: 'pointer', color: '#3e7cd3' }}>切片</b>
+      let Jude: any = <b onClick={() => judge(model)} style={{ cursor: 'pointer', color: '#3e7cd3' }}>判断</b>
+      let Tran: any = <b onClick={() => transform(model)} style={{ cursor: 'pointer', color: '#3e7cd3' }}>转geojson</b>
+      let Tf: any = (
         <Fragment>
-          <Dialog title="数据切片" show={showDialog} stateChange={(enter: boolean) => { setShowDialog(false); enter && section(model) }}>
-            <div className={styles.dialogitem + ' ' + styles.min}>
-              <label>层级设置</label>
-              <input type="text" defaultValue={min} onBlur={ev => setMin(ev.target)} placeholder="最小层级" />
-              <input type="text" defaultValue={max} onBlur={ev => setMax(ev.target)} placeholder="最大层级" />
+          <Dialog title="服务发布" show={this.state.showDialog} stateChange={(enter: boolean) => { this.setShowDialog(false); enter && transfer(model) }}>
+            <div className={styles.dialogitem}>
+              <label>图层名称</label>
+              <input type="text" defaultValue={text} onChange={ev => setText(ev.target)} placeholder="图层名称" />
             </div>
           </Dialog>
-          <b style={{ cursor: 'pointer', color: '#3e7cd3' }} onClick={() => setShowDialog(true)}>切片</b>
+          <b style={{ cursor: 'pointer', color: '#3e7cd3' }} onClick={() => this.setShowDialog(true)}>发布</b>
         </Fragment>
       )
-    }
 
-    Zip = model.ajaxStatue === ZIPING ? '解压中…' : model.ajaxStatue === ZIPSUCCESS ? '成功解压' : Zip
-    Sect = model.ajaxStatue === SECTIONING ? '切片中…' : model.ajaxStatue === SECTIONSUCCESS ? '成功切片' : Sect
-    Jude = model.ajaxStatue === JUDGEING ? '判断中…' : model.ajaxStatue === JUGESUCCESS ? '成功判断' : Jude
-    Tran = model.ajaxStatue === TRANING ? '转换中…' : model.ajaxStatue === TRANSUCCESS ? '成功转换' : Tran
-    Tf = model.ajaxStatue === TRANSFERING ? '移动中…' : model.ajaxStatue === TRANSFEREERR ? '成功移动' : Tf
-
-    if (model.status === 9) {
-      Jude = '判断中…'
-    }
-    return (
-      <Item key={model.id} {...model} >
-        {() => (
+      if (region) {
+        Sect = (
           <Fragment>
-            {api.zip && (model.status === 1 && Zip)}
-            {api.judge && ((model.status === 2 || model.status === 9) && Jude)}
-            {api.transform && ((model.status === 3 || model.status === 7) && Tran)}
-            {api.section && (model.status === 4 && Sect)}
-            {api.transfer && ((model.status === 5 || model.status === 10) && Tf)}
-            {privItem && privItem(model)}
-            <b onClick={() => delHandle(model)} style={{ cursor: 'pointer', color: '#f46579' }}>删除</b>
+            <Dialog title="数据切片" show={this.state.showDialog} stateChange={(enter: boolean) => { this.setShowDialog(false); enter && section(model) }}>
+              <div className={styles.dialogitem + ' ' + styles.min}>
+                <label>层级设置</label>
+                <input type="text" defaultValue={min} onBlur={ev => setMin(ev.target)} placeholder="最小层级" />
+                <input type="text" defaultValue={max} onBlur={ev => setMax(ev.target)} placeholder="最大层级" />
+              </div>
+            </Dialog>
+            <b style={{ cursor: 'pointer', color: '#3e7cd3' }} onClick={() => this.setShowDialog(true)}>切片</b>
           </Fragment>
-        )}
-      </Item>
-    )
-  }
+        )
+      }
 
+      Zip = model.ajaxStatue === ZIPING ? '解压中…' : model.ajaxStatue === ZIPSUCCESS ? '成功解压' : Zip
+      Sect = model.ajaxStatue === SECTIONING ? '切片中…' : model.ajaxStatue === SECTIONSUCCESS ? '成功切片' : Sect
+      Jude = model.ajaxStatue === JUDGEING ? '判断中…' : model.ajaxStatue === JUGESUCCESS ? '成功判断' : Jude
+      Tran = model.ajaxStatue === TRANING ? '转换中…' : model.ajaxStatue === TRANSUCCESS ? '成功转换' : Tran
+      Tf = model.ajaxStatue === TRANSFERING ? '发布中…' : model.ajaxStatue === TRANSFEREERR ? '成功发布' : Tf
+
+      if (model.status === 9) {
+        Jude = '判断中…'
+      }
+
+      if (model.status === 6) {
+        Sect = '切片中…'
+      }
+
+      if (model.status === 13) {
+        Tf = '发布中…'
+      }
+
+      return (
+        <Item key={model.id} {...model} >
+          {() => (
+            <Fragment>
+              {api.zip && (model.status === 1 && Zip)}
+              {api.judge && ((model.status === 2 || model.status === 9) && Jude)}
+              {api.transform && ((model.status === 3 || model.status === 7) && Tran)}
+              {api.section && (model.status === 4 && Sect)}
+              {api.transfer && ((model.status === 5 || model.status === 10) && Tf)}
+              {privItem && privItem(model)}
+              {(
+                model.state === 9 || model.status === 6 || model.status === 13 || 
+                model.ajaxStatue === ZIPING || model.ajaxStatue === SECTIONING || 
+                model.ajaxStatue === JUDGEING || model.ajaxStatue === TRANING || 
+                model.ajaxStatue === TRANSFERING) ? '' : <b onClick={() => delHandle(model)} style={{ cursor: 'pointer', color: '#f46579' }}>删除</b>
+              }
+            </Fragment>
+          )}
+        </Item>
+      )
+    }
+  }
   return {
     ItemFn,
     text

+ 1 - 1
src/page/List/ListState/action.ts

@@ -106,7 +106,7 @@ export const sectionItemAction = async (dispatch: Function, url: string, item: M
 
 export const sectionStepAction = async (dispatch: Function, url: string, item: Model) => {
   let res = await http.get(url)
-  if (res.data.data) {
+  if (res.data.data && res.data.data.progress) {
     sessionStorage.setItem(res.data.data.id, res.data.data.progress)
   }
   return res.data

+ 9 - 1
src/page/List/ModelList.tsx

@@ -3,8 +3,9 @@ import GrentReducer from './grent'
 import Upload from '../../components/Upload'
 import styles from './index.module.css'
 
+
 export default function ModelList({ className }: any) {
-  const { referData, Element } = GrentReducer({
+  const { referData, Element, models } = GrentReducer({
     delUrl: '/fdModel/delete/',
     getUrl: '/fdModel/list/',
     zipUrl: '/fdModel/unzip/',
@@ -12,6 +13,13 @@ export default function ModelList({ className }: any) {
     transferUrl: '/fdModel/move/'
   })
 
+  for (let i = 0; i < models.length; i++) {
+    let model = models[i] as Model
+    if (model.status !== 6) continue
+    setTimeout(() => referData(), 1000)
+    break;
+  }
+
   return (
     <div className={className}>
       <div className={styles.uplayer} >

+ 9 - 5
src/page/List/grent.tsx

@@ -54,8 +54,10 @@ interface GrentApi {
   ItemFn?: Function,
   region?: boolean
 }
-export default function GrentReducer({ getUrl, delUrl, zipUrl, sectionUrl, transformUrl, transferUrl, judgeUrl, ItemFn, region }: GrentApi) {
-  let [showDialog, setShowDialog] = useState(false)
+
+export default function GrentReducer1({ getUrl, delUrl, zipUrl, sectionUrl, transformUrl, transferUrl, judgeUrl, ItemFn, region }: GrentApi) {
+  let [showDialog1, setShowDialog1] = useState(false)
+  let [showDialog2, setShowDialog2] = useState(false)
   let [referCount, setReferCount] = useState(0)
   let [modelState, modelDispatch] = useReducer(reducer, initialState())
   const models = getList(modelState)
@@ -89,12 +91,14 @@ export default function GrentReducer({ getUrl, delUrl, zipUrl, sectionUrl, trans
           delHandle,
           api: { zip: zipUrl, section: sectionUrl, transfer: transferUrl, judge: judgeUrl, transform: transformUrl },
           region,
-          showDialog, 
-          setShowDialog
+          showDialog1, 
+          setShowDialog1,
+          showDialog2,
+          setShowDialog2
         })
         return (
           <Fragment key={model.id}>
-            {PublicItemFn(model, ItemFn)}
+            <PublicItemFn model={model} privItem={ItemFn} />
           </Fragment>
         )
         }}

+ 41 - 0
src/page/Login/index.tsx

@@ -0,0 +1,41 @@
+import React, {useState} from 'react'
+import style from './style.module.css'
+import http from '../../http'
+import {setToken} from '../../http'
+import {history} from '../../router.config'
+
+function Login() {
+  let [user, setUser] = useState('');
+  let [psw, setPsw] = useState('');
+
+  const submission = async () => {
+    let {status, data, message} = (await http({ method: 'post', url: '/login', params: {username: user, password: psw} })).data
+    // let {status, data, message} = (await http.post('/login', {params: {username: user, password: psw}})).data
+    if (status === 200) {
+      setToken(data)
+      history.replace('/gis')
+    } else {
+      alert(message)
+    }
+  }
+
+  return (
+    <div className={style.layer}>
+      <h1>登录</h1>
+      <div>
+        <label htmlFor="user">用户名</label>
+        <input type="text" name="user" value={user} onChange={ev => setUser(ev.target.value)}/>
+      </div>
+      <div>
+        <label htmlFor="psw">密码</label>
+        <input type="password" name="psw" value={psw} onChange={ev => setPsw(ev.target.value)}/>
+      </div>
+
+      <div>
+        <a className={style.button} onClick={submission}>登录</a>
+      </div>
+    </div>
+  )
+}
+
+export default Login

+ 71 - 0
src/page/Login/style.module.css

@@ -0,0 +1,71 @@
+
+
+.layer {
+  position: absolute;
+  left: 50%;
+  top: 50%;
+  padding: 20px 30px;
+  box-shadow: 0 0 10px rgba(255,255,255,0.8);
+  transform: translateX(-50%) translateY(-50%);
+  min-width: 300px;
+  border-radius: 10px;
+  border: 1px solid rgba(255,255,255,0.8);
+}
+
+.layer h1 {
+  font-size: 22px;
+  text-align: center;
+  margin-bottom: 20px;
+}
+
+.layer > div {
+  position: relative;
+  padding-left: 70px;
+  margin-top: 10px;
+}
+
+.layer > div > label {
+  width: 70px;
+  position: absolute;
+  left: 0;
+  top: 50%;
+  transform: translateY(-50%);
+}
+
+.layer > div > input {
+  width: 100%;
+  box-sizing: border-box;
+  margin: 0;
+  padding-top: 8px;
+  padding-bottom: 8px;
+}
+
+
+
+.button {
+  display: inline-block;
+  line-height: 1;
+  white-space: nowrap;
+  cursor: pointer;
+  border: 1px solid #dcdfe6;
+  -webkit-appearance: none;
+  text-align: center;
+  box-sizing: border-box;
+  outline: none;
+  margin: 0;
+  transition: .1s;
+  font-weight: 500;
+  color: #fff;
+  background-color: #409eff;
+  border-color: #409eff;
+  padding: 9px 15px;
+  font-size: 12px;
+  border-radius: 3px;
+  margin-top: 20px;
+}
+
+.button:hover {
+  background: #66b1ff;
+  border-color: #66b1ff;
+  color: #fff;
+}

+ 36 - 27
src/page/StyleEdit/index.tsx

@@ -2,6 +2,8 @@ import React, { useReducer, useEffect, useState, Fragment } from 'react'
 import { RouteComponentProps } from 'react-router'
 import { reducer, initialState, getLayersAction, updateLayerAction, saveLayersAction, Item } from './reducer'
 import VectorShow from '../../components/VectorShow'
+import Color from '../../components/Color'
+import styles from './style.module.css'
 
 type Attr = 'lineColor' | 'lineWidth' | 'fillColor' | 'show'
 type EvAttr = 'value' | 'checked'
@@ -16,8 +18,8 @@ function StyleEdit(props: RouteComponentProps) {
   useEffect(() => { getLayersAction(dispatch, params.id) }, [params.id])
   useEffect(() => { layer || setLayer(layers[0]) }, [layer, layers])
 
-  const changeHandle = (attr: Attr, evAttr: EvAttr = 'value') => (ev: React.ChangeEvent<HTMLInputElement>) => {
-    updateLayerAction(dispatch, layer, { ...style, [attr]: ev.target[evAttr] })
+  const changeHandle = (attr: Attr, evAttr: EvAttr = 'value', init = false) => (ev: any) => {
+    updateLayerAction(dispatch, layer, { ...style, [attr]: init ? ev : ev.target[evAttr] })
   }
   const saveHandle = async () => {{
     if (await saveLayersAction(style, params.id)) {
@@ -30,31 +32,38 @@ function StyleEdit(props: RouteComponentProps) {
 
   return (
     <div>
-      <select value={layer} onChange={ev => setLayer(ev.target.value)}>
-        {layers.map(layer => <option value={layer} key={layer}>{layer}</option>)}
-      </select>
-      {style && (
-        <Fragment>
-          <div>
-            <label>填充颜色</label>
-            <input type="color" value={style.fillColor} onChange={changeHandle('fillColor')} />
-          </div>
-          <div>
-            <label>线条颜色</label>
-            <input type="color" value={style.lineColor} onChange={changeHandle('lineColor')} />
-          </div>
-          <div>
-            <label>线条厚度</label>
-            <input type="number" value={style.lineWidth} onChange={changeHandle('lineWidth')} />
-          </div>
-          <div>
-            <label>是否显示</label>
-            <input type="checkbox" checked={style.show} onChange={changeHandle('show', 'checked')} />
-          </div>
-          <button onClick={saveHandle}>发布</button>
-        </Fragment>
-      )}
-      {style && <VectorShow {...verctorInfo} height={2500} />}
+      <div className={styles.filter}>
+        <div className={styles.inputitem}>
+          <label>图层选择</label>
+          <select value={layer} onChange={ev => setLayer(ev.target.value)}>
+            {layers.map(layer => <option value={layer} key={layer}>{layer}</option>)}
+          </select>
+        </div>
+        {style && (
+          <Fragment>
+            <div className={styles.inputitem}>
+              <label>填充颜色</label>
+              <Color color={style.fillColor} onChange={changeHandle('fillColor', void 0, true)} />
+            </div>
+            <div className={styles.inputitem}>
+              <label>线条颜色</label>
+              <Color color={style.lineColor} onChange={changeHandle('lineColor', void 0, true)} />
+            </div>
+            <div className={styles.inputitem}>
+              <label>线条厚度</label>
+              <input type="number" value={style.lineWidth} onChange={changeHandle('lineWidth')} />
+            </div>
+            <div className={styles.inputitem}>
+              <label>是否显示</label>
+              <input type="checkbox" checked={style.show} onChange={changeHandle('show', 'checked')} />
+            </div>
+            <div className={styles.inputitem}>
+              <button onClick={saveHandle}>发布</button>
+            </div>
+            </Fragment>
+        )}
+      </div>
+      {style && <div className={styles.content}><VectorShow {...verctorInfo} height={2500} /></div>}
     </div>
   )
 }

+ 1 - 1
src/page/StyleEdit/reducer.ts

@@ -7,7 +7,7 @@ export const UPDATELAYER = Symbol('update_layer')
 export const DELLAYER = Symbol('del_layer')
 export const LOADLAYERS = Symbol('load_layers')
 
-const PREV = process.env.NODE_ENV === 'development' ? '/test/a' : '/a'
+const PREV = process.env.NODE_ENV === 'development' ? '/test/data' : '/data'
 
 export interface Item extends LayerStyle {
   show: boolean,

+ 59 - 0
src/page/StyleEdit/style.module.css

@@ -0,0 +1,59 @@
+.inputitem {
+  display: inline-block;
+  font-size: 13px;
+  margin-right: 15px;
+}
+
+.inputitem select,
+.inputitem span,
+.inputitem input {
+  height: 32px;
+  line-height: 24px;
+  margin: 0 8px;
+  vertical-align: middle;
+  border-radius: 3px;
+  box-sizing: border-box;
+}
+
+
+.filter {
+  margin-bottom: 15px;
+  margin-top: 20px;
+  background-color: #1b1b1b;
+  padding: 25px 18px;
+}
+
+.inputitem button {
+  display: inline-block;
+  line-height: 1;
+  white-space: nowrap;
+  cursor: pointer;
+  border: 1px solid #dcdfe6;
+  -webkit-appearance: none;
+  text-align: center;
+  box-sizing: border-box;
+  outline: none;
+  margin: 0;
+  transition: .1s;
+  font-weight: 500;
+  color: #fff;
+  background-color: #409eff;
+  border-color: #409eff;
+  padding: 9px 15px;
+  font-size: 12px;
+  border-radius: 3px;
+}
+
+.inputitem button:hover {
+  background: #66b1ff;
+  border-color: #66b1ff;
+  color: #fff;
+}
+
+
+.content {
+  background-color: #1b1b1b;
+  padding: 24px;
+  border-radius: 3px;
+  margin-top: 15px;
+}

+ 4 - 1
src/router.config.ts

@@ -1,3 +1,5 @@
+import { createHashHistory } from 'history'
+
 interface RouteItem {
   title: string,
   path: string,
@@ -6,6 +8,7 @@ interface RouteItem {
   parent?: RouteItem,
   component: any
 }
+export const history = createHashHistory()
 
 const config: Array<RouteItem> = [
   {
@@ -39,4 +42,4 @@ config.push({
   parent: config[2]
 })
 
-export default config
+export default config

+ 2 - 2
src/setupProxy.js

@@ -4,7 +4,7 @@ module.exports = app => {
   app.use(
     proxy('/api', {
       // target: 'http://192.168.0.10:8082',
-      target: 'http://39.108.123.31:8082',
+      target: 'http://47.107.252.54:8082',
       changeOrigin: true,
       pathRewrite: {
         "^/api": "/api"
@@ -15,7 +15,7 @@ module.exports = app => {
   app.use(
     proxy('/test', {
       // target: 'http://192.168.0.10:8082',
-      target: 'http://39.108.123.31:8082',
+      target: 'http://47.107.252.54:8082',
       changeOrigin: true,
       pathRewrite: {
         "^/test": "/"