Просмотр исходного кода

【模版目录】更新管理端文件

吴昊天 3 лет назад
Родитель
Сommit
9d4a82d5ad

Разница между файлами не показана из-за своего большого размера
+ 153 - 153
template/admin/package-lock.json


+ 3 - 1
template/admin/package.json

@@ -38,6 +38,8 @@
     "lodash": "^4.17.10",
     "marked": "^0.3.9",
     "moment": "^2.29.1",
+    "monaco-editor": "^0.28.1",
+    "monaco-editor-webpack-plugin": "^4.2.0",
     "oss": "0.0.1",
     "qiniu-js": "^2.5.5",
     "qrcodejs2": "0.0.2",
@@ -50,7 +52,7 @@
     "uglifyjs-webpack-plugin": "^2.2.0",
     "v-org-tree": "^1.0.6",
     "v-viewer": "^1.5.1",
-    "view-design": "4.1.0",
+    "view-design": "^4.4.0",
     "vue": "^2.5.10",
     "vue-awesome-swiper": "^4.1.1",
     "vue-codemirror": "^4.0.6",

+ 44 - 0
template/admin/src/api/system.js

@@ -358,6 +358,50 @@ export function savefileApi(data) {
 	file_edit:true
   });
 }
+/**
+ * @description 文件管理 -- 新建文件夹
+ */
+export function createFolder(params) {
+  return request({
+    url: `system/file/createFolder`,
+    method: 'GET',
+    params,
+	file_edit:true
+  });
+}
+/**
+ * @description 文件管理 -- 新建文件
+ */
+export function createFile(params) {
+  return request({
+    url: `system/file/createFile`,
+    method: 'GET',
+    params,
+	file_edit:true
+  });
+}
+/**
+ * @description 文件管理 -- 删除文件或文件夹
+ */
+export function rename(params) {
+  return request({
+    url: `system/file/rename`,
+    method: 'GET',
+    params,
+	file_edit:true
+  });
+}
+/**
+ * @description 文件管理 -- 删除文件或文件夹
+ */
+export function delFolder(params) {
+  return request({
+    url: `system/file/delFolder`,
+    method: 'GET',
+    params,
+	file_edit:true
+  });
+}
 
 /**
  * @description 安全维护 -- 更换域名

template/admin/src/assets/images/kfbg.jpg → template/admin/src/assets/images/kfbg_1.jpg


BIN
template/admin/src/assets/images/kfbg_2.jpg


+ 4 - 1
template/admin/src/components/copyright/index.vue

@@ -3,7 +3,7 @@
     <div class="ivu-global-footer-links">
       <a :href="item.href" target="_blank" v-for="(item, index) in links" :key="index">{{ item.title }}</a>
     </div>
-    <div class="ivu-global-footer-copyright">{{ copyright }}</div>
+    <div class="ivu-global-footer-copyright"><a href="https://www.crmeb.com" target="_blank">{{ copyright }}</a></div>
   </div>
 </template>
 <script>
@@ -72,4 +72,7 @@ export default {
   color: #808695;
   font-size: 14px;
 }
+.ivu-global-footer-copyright a {
+  color: #808695;
+}
 </style>

+ 11 - 3
template/admin/src/pages/account/login/index.vue

@@ -60,7 +60,8 @@
       ref="verify"
     ></Verify>
     <div class="footer">
-      <div class="pull-right">{{copyright}}</div>
+      <div class="pull-right" v-if="copyright">{{copyright}}</div>
+      <div class="pull-right" v-else><a href="https://www.crmeb.com" target="_blank">Copyright © 2022 | 西安众邦网络科技有限公司 | {{version}}</a></div>
     </div>
   </div>
 </template>
@@ -104,7 +105,8 @@ export default {
       swiperList: [],
       defaultSwiperList: require('@/assets/images/sw.jpg'),
       key: '',
-      copyright: 'Copyright © 2022 | 西安众邦网络科技有限公司',
+      copyright: '',
+      version: ''
     };
   },
   created() {
@@ -169,7 +171,8 @@ export default {
           this.login_logo = data.login_logo ? data.login_logo : require('@/assets/images/logo.png');
           this.swiperList = data.slide.length ? data.slide : [{ slide: this.defaultSwiperList }];
           this.key = data.key;
-          if(data.copyright != '') this.copyright = data.copyright;
+          this.copyright = data.copyright;
+          this.version = data.version;
         })
         .catch((err) => {
           this.$Message.error(err);
@@ -454,9 +457,14 @@ a:link, a:visited, a:hover, a:active {
   overflow: hidden;
   padding: 10px 20px;
   height: 36px;
+  z-index: 999;
 }
 .pull-right {
     float: right!important;
+    color: #666;
+}
+.pull-right a {
+    color: #666;
 }
 .footer{
   position: fixed;

+ 184 - 115
template/admin/src/pages/kefu/index.vue

@@ -1,6 +1,13 @@
 <template>
   <div class="wrapper-box">
     <div class="page-account kf">
+      <div class="content">
+        <img :src="copyrightImg" alt="" />
+        <div class="desc">
+          <p class="tit">让客户服务如此简单</p>
+          <p class="kefu">专业客服系统<br />助力企业打造一流的服务体验</p>
+        </div>
+      </div>
       <div class="container" :class="[fullWidth > 768 ? 'containerSamll' : 'containerBig']">
         <div class="index_from page-account-container">
           <div :style="{ display: !loginType ? 'block' : 'none' }">
@@ -43,7 +50,10 @@
       <!--                </div>-->
       <!--            </Modal>-->
     </div>
-    <div class="foot-box">Copyright © 2022 西安众邦网络科技有限公司 | {{ version }} 客服系统</div>
+    <div class="foot-box" v-if="copyright">{{ copyright }}</div>
+    <div class="foot-box" v-else>
+      <a href="https://www.crmeb.com" target="_blank">Copyright © 2022 | 西安众邦网络科技有限公司 | {{ version }}</a>
+    </div>
   </div>
 </template>
 <script>
@@ -88,14 +98,20 @@ export default {
       version: '', //版本号
       isScan: false,
       timeNum: 0,
+      copyright: '',
+      copyrightImg: require('@/assets/images/logo-dark.png'),
     };
   },
   created() {
     kefuConfig().then((res) => {
       this.version = res.data.version;
+      this.copyright = res.data.copyright;
       if (res.data.site_name) {
         document.title = res.data.site_name;
       }
+      if (res.data.copyrightImg) {
+        this.copyrightImg = res.data.copyrightImg;
+      }
     });
     this.isMobile = this.$store.state.media.isMobile;
     var _this = this;
@@ -302,177 +318,225 @@ export default {
 </script>
 <style scoped lang="stylus">
 .page-account {
+  display: flex;
+  width: 100%;
+  background-image: url('~@/assets/images/kfbg_2.jpg');
+  background-size: cover;
+  background-position: center;
+  justify-content: center;
+  align-items: center;
+  height: 100vh;
+  overflow: auto;
+
+  .content {
+    height: 400px;
+    margin-right: 100px;
+
+    .desc {
+      color: #fff;
+
+      .tit {
+        font-size: 40px;
+        font-weight: 600;
+      }
+
+      .kefu {
+        margin-top: 30px;
+        font-weight: 500;
+        font-size: 20px;
+      }
+    }
+
+    img {
+      width: 360px;
+      margin-left: -100px;
+    }
+  }
+}
+
+.code-box {
+  position: relative;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+
+  .qrcode {
     display: flex;
-    width: 100%;
-    background-image: url('~@/assets/images/kfbg.jpg');
-    background-size: cover;
-    background-position: center;
-    flex-direction: column;
+    align-items: center;
     justify-content: center;
+    width: 180px;
+    height: 180px;
+    border: 1px solid #E5E5E6;
+  }
+
+  .rxpired-box {
+    display: flex;
+    flex-direction: column;
     align-items: center;
-    height: 100vh;
-    overflow: auto;
+    justify-content: center;
+    position: absolute;
+    left: 50%;
+    top: 50%;
+    transform: translate(-50%, -50%);
+    width: 160px;
+    height: 160px;
+    background: rgba(0, 0, 0, 0.6);
+
+    p {
+      margin-bottom: 10px;
+      font-size: 15px;
+      color: #fff;
+    }
+  }
 }
-.code-box
-    position relative
-    display flex
-    align-items center
-    justify-content center
-    .qrcode
-        display flex
-        align-items center
-        justify-content center
-        width 180px
-        height 180px
-        border 1px solid #E5E5E6
-    .rxpired-box
-        display flex
-        flex-direction column
-        align-items center
-        justify-content center
-        position absolute
-        left 50%
-        top 50%
-        transform translate(-50%,-50%)
-        width 160px
-        height 160px
-        background rgba(0,0,0,0.6)
-        p
-            margin-bottom 10px
-            font-size 15px
-            color #fff
-.page-account-top-logo
-    color #000000
-    font-size 21px
-.wrapper-box
-    display flex
-    flex-direction column
-    height: 100vh
-    .foot-box
-        padding 20px 0
-        font-size 14px
-        color #666666
-        text-align center
-        box-sizing border-box
+
+.page-account-top-logo {
+  color: #000000;
+  font-size: 21px;
+}
+
+.wrapper-box {
+  display: flex;
+  flex-direction: column;
+  height: 100vh;
+
+  .foot-box {
+    padding: 20px 0;
+    font-size: 14px;
+    color: #666666;
+    text-align: center;
+    box-sizing: border-box;
+
+    a {
+      color: #666666;
+    }
+  }
+}
+
 .page-account {
-    display: flex;
-    flex 1
+  display: flex;
+  flex: 1;
 }
 
 .page-account .code {
-    display: flex;
-    align-items: center;
-    justify-content: center;
+  display: flex;
+  align-items: center;
+  justify-content: center;
 }
 
 .page-account .code .pictrue {
-    height: 40px;
+  height: 40px;
 }
 
 .swiperPross {
-    border-radius: 6px 0px 0px 6px;
+  border-radius: 6px 0px 0px 6px;
 }
 
 .swiperPross, .swiperPic, .swiperPic img {
-    width: 510px;
-    height: 100%;
+  width: 510px;
+  height: 100%;
 }
 
 .swiperPic img {
-    width: 100%;
-    height: 100%;
+  width: 100%;
+  height: 100%;
 }
 
 .container {
-    height: 400px !important;
-    padding: 0 !important;
-    /*overflow: hidden;*/
-    border-radius: 6px;
-    z-index: 1;
-    display: flex;
+  height: 400px !important;
+  padding: 0 !important;
+  /* overflow: hidden; */
+  border-radius: 6px;
+  z-index: 1;
+  display: flex;
 }
 
 .containerSamll {
-    width: 384px !important;
-    margin-left 30%
-    background: #fff !important;
+  width: 384px !important;
+  // margin-left: 30%;
+  background: #fff !important;
 }
 
 .containerBig {
-    width: 90%;
-    padding-bottom 20px
-    margin-top: 84px;
-    background: #f7f7f7 !important;
-    height auto!important
-    box-shadow: 0px 3px 20px rgba(0, 20, 41, 0.06);
+  width: 90%;
+  padding-bottom: 20px;
+  margin-top: 84px;
+  background: #f7f7f7 !important;
+  height: auto !important;
+  box-shadow: 0px 3px 20px rgba(0, 20, 41, 0.06);
 }
 
 .index_from {
-    position: relative;
-    padding: 40px 40px 32px 40px;
-    height: 400px;
-    width 100%
-    box-sizing: border-box;
+  position: relative;
+  padding: 40px 40px 32px 40px;
+  height: 400px;
+  width: 100%;
+  box-sizing: border-box;
 }
-.containerBig .index_from{
-    padding 20px
-    height auto!important
+
+.containerBig .index_from {
+  padding: 20px;
+  height: auto !important;
 }
-.index_from .qh_box
-    position:absolute;
-    right 12px
-    top 0
-    cursor pointer
-    .iconfont
-        color #265BED
-        font-size 36px
 
+.index_from .qh_box {
+  position: absolute;
+  right: 12px;
+  top: 0;
+  cursor: pointer;
+
+  .iconfont {
+    color: #265BED;
+    font-size: 36px;
+  }
+}
 
 .page-account-top {
-    padding: 20px 0 50px !important;
-    box-sizing: border-box !important;
-    display: flex;
-    justify-content: center;
+  padding: 20px 0 50px !important;
+  box-sizing: border-box !important;
+  display: flex;
+  justify-content: center;
 }
 
 .page-account-container {
-    border-radius: 0px 6px 6px 0px;
+  border-radius: 0px 6px 6px 0px;
 }
 
 .btn {
-    background: #265BED;
+  background: #265BED;
 }
 
 .captchaBox {
-    width: 310px;
+  width: 310px;
 }
 
 input {
-    display: block;
-    width: 290px;
-    line-height: 40px;
-    margin: 10px 0;
-    padding: 0 10px;
-    outline: none;
-    border: 1px solid #c8cccf;
-    border-radius: 4px;
-    color: #6a6f77;
+  display: block;
+  width: 290px;
+  line-height: 40px;
+  margin: 10px 0;
+  padding: 0 10px;
+  outline: none;
+  border: 1px solid #c8cccf;
+  border-radius: 4px;
+  color: #6a6f77;
 }
 
 #msg {
-    width: 100%;
-    line-height: 40px;
-    font-size: 14px;
-    text-align: center;
+  width: 100%;
+  line-height: 40px;
+  font-size: 14px;
+  text-align: center;
 }
 
 a:link, a:visited, a:hover, a:active {
-    margin-left: 100px;
-    color: #0366D6;
+  margin-left: 100px;
+  color: #0366D6;
 }
 
-.index_from >>> .ivu-input-large
-    font-size:14px!important
+.index_from >>> .ivu-input-large {
+  font-size: 14px !important;
+}
 </style>
 <style>
 @media screen and (min-width: 320px) and (max-width: 960px) {
@@ -481,6 +545,11 @@ a:link, a:visited, a:hover, a:active {
     background-size: 100% auto !important;
     background-repeat: no-repeat;
     background-position: left top !important;
+    display: flex;
+  }
+
+  .content {
+    display: none;
   }
   .index_from {
     box-shadow: 0px 3px 20px rgba(0, 20, 41, 0.06);

+ 6 - 5
template/admin/src/pages/setting/multiLanguage/langList.vue

@@ -102,11 +102,9 @@
             <Radio :label="item.value" v-for="(item, index) in langType.isAdmin" :key="index">{{ item.title }}</Radio>
           </RadioGroup>
         </FormItem>
-        <FormItem label="状态码/文字:" prop="code" class="mb20">
-          <Input v-model="langFormData.code" placeholder="请输入状态码/文字" style="width: 330px"></Input>
-        </FormItem>
-        <FormItem label="文字:" prop="remarks" class="mb20">
-          <Input v-model="langFormData.remarks" placeholder="请输入状态码/文字" style="width: 330px"></Input>
+        <Input v-model="langFormData.edit" type="hidden"></Input>
+        <FormItem label="语言说明:" prop="remarks" class="mb20">
+          <Input v-model="langFormData.remarks" placeholder="请输入语言说明" style="width: 330px"></Input>
         </FormItem>
         <FormItem label="对应语言:" prop="remark" class="mb20">
           <Table
@@ -195,6 +193,8 @@ export default {
       langFormData: {
         is_admin: 0,
         code: '',
+        remarks: '',
+        edit: 0,
         list: [],
       },
       tabList: [],
@@ -242,6 +242,7 @@ export default {
           this.langFormData.list = res.data.list;
           this.langFormData.code = res.data.code;
           this.langFormData.remarks = res.data.remarks;
+          this.langFormData.edit = 1;
           this.addlangModal = true;
         })
         .catch((err) => {

+ 587 - 170
template/admin/src/pages/system/maintain/systemFile/opendir.vue

@@ -7,7 +7,7 @@
 		</div>
 		<Card :bordered="false" dis-hover class="ivu-mt">
 			<login-from v-if="isShowLogn" @on-Login="onLogin"></login-from>
-			<div v-if="isShowList" class="backs" @click="goBack">
+			<div v-if="isShowList" class="backs" @click="goBack(false)">
 				<Icon type="ios-folder-outline" class="mr5" /><span>返回上级</span>
 			</div>
 			<Table v-if="isShowList" ref="selection" :columns="columns4" :data="tabList" :loading="loading"
@@ -27,23 +27,62 @@
 				</template>
 			</Table>
 		</Card>
-		<!-- <codemirror :rows='rows' :code='code' :modals='modals' :title='title'></codemirror> -->
-		<Modal v-model="modals" scrollable footer-hide closable :title="title" :mask-closable="false" width="900">
-			<Button type="primary" id="savefile" class="mr5 mb15" @click="savefile">保存</Button>
-			<Button id="undo" class="mr5 mb15" @click="undofile">撤销</Button>
-			<Button id="redo" class="mr5 mb15" @click="redofile">回退</Button>
-			<Button id="refresh" class="mb15" @click="refreshfile">刷新</Button>
-			<div class="file-box">
-				<div class="file-left cm-s-ambiance CodeMirror">
-					<Tree :data="navList" :render="renderContent" :load-data="loadData" expand-node></Tree>
+		<Modal :class-name="className" v-model="modals" scrollable footer-hide closable :mask-closable="false" width="80%">
+			<p slot="header" class="diy-header" ref="diyHeader">
+				<span>{{title}}</span>
+				<Icon @click="winChanges" class="diy-header-icon" :type="className ? 'ios-contract' : 'ios-qr-scanner'"  size = "20"/>
+			</p>
+			<div style="height: 100%;">
+				<Button type="primary" id="savefile" class="diy-button" @click="savefile">保存</Button>
+				<Button id="refresh" class="diy-button" @click="refreshfile">刷新</Button>
+				
+				<div class="file-box">
+					<div class="show-info">
+						<div class="show-text" :title="navItem.pathname">
+							目录: {{navItem.pathname}}
+						</div>
+						<div class="diy-button-list">
+							<Button class="diy-button" @click="goBack(true)">返回上一级</Button>
+							<Button class="diy-button" @click="getList(true,true)">刷新</Button>
+						</div>
+					</div>
+					<div class="file-left">
+						<Tree class="diy-tree-render" :data="navList" :render="renderContent" :load-data="loadData" @on-contextmenu="handleContextMenu" expand-node>
+							<template class="diy-menu" slot="contextMenu">
+								<DropdownItem v-if="contextData && contextData.isDir" @click.native="handleContextCreateFolder()">新建文件夹</DropdownItem>
+								<DropdownItem v-if="contextData && contextData.isDir" @click.native="handleContextCreateFile()">新建文件</DropdownItem>
+								<DropdownItem @click.native="handleContextRename()">重命名</DropdownItem>
+								<DropdownItem @click.native="handleContextDelFolder()" style="color: #ed4014">删除</DropdownItem>
+							</template>
+						</Tree>
+					</div>
+					<div class="file-fix"></div>
+					<div class="file-content">
+						<div id="container" style="height:100%;min-height: 600px;"></div>
+					</div>
+					<Spin size="large" fix v-if="spinShow"></Spin>
 				</div>
-				<div class="file-content">
-					<textarea ref="mycode" class="codesql public_text" v-model="code"></textarea>
-				</div>
-				<Spin size="large" fix v-if="spinShow"></Spin>
 			</div>
-
+			
 		</Modal>
+		
+		<div v-show="formShow" class="diy-from">
+			<div class="diy-from-header">{{formTitle}}<span :title="contextData ? contextData.pathname : ''">{{contextData ? contextData.pathname : ''}}</span></div>
+			<Form ref="formInline" :model="formFile" :rules="ruleInline" inline>
+				<FormItem prop="filename" class="diy-file">
+					<Input type="text" class="diy-file" v-model="formFile.filename" placeholder="请输入名字">
+						<Icon type="ios-folder-open-outline" slot="prepend"></Icon>
+					</Input>
+				</FormItem>
+				<FormItem>
+					<Button class="diy-button" @click="handleSubmit('formInline')">确定</Button>
+				</FormItem>
+				<FormItem>
+					<Button class="diy-button" @click="formExit()">取消</Button>
+				</FormItem>
+				<div class="form-mask" v-show="formShow"></div>
+			</Form>
+		</div>		
 	</div>
 </template>
 
@@ -53,43 +92,21 @@
 		opendirListApi,
 		openfileApi,
 		savefileApi,
-		opendirLoginApi
+		opendirLoginApi,
+		createFolder,
+		createFile,
+		delFolder,
+		rename
 	} from '@/api/system';
 	import CodeMirror from 'codemirror/lib/codemirror';
 	import loginFrom from './components/loginFrom';
-	import codemirror from './components/codemirror';
-	import 'codemirror/theme/ambiance.css';
 	import {
 		setCookies,
 		getCookies,
 		removeCookies
 	} from '@/libs/util';
-
-
-	// 核心样式
-	// import 'codemirror/lib/codemirror.css'
-	// 引入主题后还需要在 options 中指定主题才会生效
-	import 'codemirror/theme/cobalt.css'
-
-	// 需要引入具体的语法高亮库才会有对应的语法高亮效果
-	// codemirror 官方其实支持通过 /addon/mode/loadmode.js 和 /mode/meta.js 来实现动态加载对应语法高亮库
-	// 但 vue 貌似没有无法在实例初始化后再动态加载对应 JS ,所以此处才把对应的 JS 提前引入
-	// import 'codemirror/mode/javascript/javascript.js'
-	// import 'codemirror/mode/css/css.js'
-	// import 'codemirror/mode/xml/xml.js'
-	// import 'codemirror/mode/clike/clike.js'
-	// import 'codemirror/mode/markdown/markdown.js'
-	// import 'codemirror/mode/python/python.js'
-	// import 'codemirror/mode/r/r.js'
-	// import 'codemirror/mode/shell/shell.js'
-	// import 'codemirror/mode/sql/sql.js'
-	// import 'codemirror/mode/swift/swift.js'
-	// import 'codemirror/mode/vue/vue.js'
-	import 'codemirror/addon/edit/closebrackets.js'
-
-
-	require('codemirror/mode/javascript/javascript');
-	// import { resolveComponent } from 'vue'
+	import Fullscreen from '@/components/main/components/fullscreen';
+	import * as monaco from 'monaco-editor'
 	export default {
 		name: 'opendir',
 		data() {
@@ -142,49 +159,40 @@
 				rows: {},
 				pathname: '',
 				title: '',
-				navList: []
+				navList: [],   //左侧导航数据
+				navItem:{},   //左侧导航点击是选中的数据
+				contextData:null,   //左侧导航右键点击是产生的数据对象
+				formFile: {      //重命名表单
+					filename: '',
+				},
+				ruleInline: {
+					filename: [
+						{ required: true, message: '请输入文件或文件夹的名字', trigger: 'blur' }
+					]
+				},
+				formShow:false,
+				// 文件操作类型 createFolder|创建文件夹 createFile|创建文件 delFolder|删除文件夹或者文件
+				fileType:'',
+				formTitle:'测试',
+				className:"",
+				fullscreen:false,  // 是否全屏
 			};
 		},
 		components: {
 			loginFrom,
-			codemirror
+			Fullscreen
 		},
 		mounted() {
-			this.editor = CodeMirror.fromTextArea(this.$refs.mycode, {
-				value: 'http://www.crmeb.com', // 文本域默认显示的文本
-				mode: 'text/javascript',
-				theme: 'ambiance', // CSS样式选择
-				indentUnit: 8, // 缩进单位,默认2
-				smartIndent: true, // 是否智能缩进
-				tabSize: 4, // Tab缩进,默认4
-				readOnly: false, // 是否只读,默认false
-				showCursorWhenSelecting: true,
-				lineNumbers: true, // 是否显示行号
-				lineWrapping: true,   //内容超过编辑器的宽时,应该滚动显示还是换行显示
-				autoCloseBrackets:true,   //代码自动补全
-				indentWithTabs: true,
-				matchBrackets: true,
-				extraKeys: {
-					'Ctrl': 'autocomplete'
-				}, //自定义快捷键
-				gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter', 'CodeMirror-lint-markers'],
-			});
-			// 初始化
-			// this._initialize()
-
-			//代码自动提示功能,记住使用cursorActivity事件不要使用change事件,这是一个坑,那样页面直接会卡死
-			editor.on('cursorActivity', function() {
-				editor.showHint()
-			})
+			this.initEditor();
+			
 		},
 		created() {
 			this.getList();
-			// this.onIsLogin();
 		},
-		methods: {
-			
-			
+		computed:{
 			
+		},
+		methods: {
 			// 点击行
 			currentChange(currentRow) {
 				if (currentRow.isDir) {
@@ -193,40 +201,63 @@
 					this.edit(currentRow);
 				}
 			},
-			// 列表
-			getList() {
-				this.loading = true;
-				opendirListApi(this.formItem)
+			/**
+			 * 文件列表
+			 * @param {Object} refresh   // 是否重新加载 bool
+			 * @param {Object} is_edit   // 是否是编辑器中的刷新 bool
+			 */
+			getList(refresh,is_edit) { 
+				let data;
+				if(refresh){
+					data = {
+						dir: '',
+						superior: 0,
+						filedir: '',
+					};
+				}else{
+					data = this.formItem;
+				}
+				if(!is_edit) this.loading = true;
+				opendirListApi(data)
 					.then(async (res) => {
 						let data = res.data;
-						this.tabList = data.list;
-						this.navList = data.navList
+						if(is_edit)
+						{
+							this.navList = data.navList;
+						}else{
+							this.navListForTab = data.navList;
+							this.tabList = data.list;
+							// this.navList = data.navList;
+							this.isShowList = true;
+						}
 						this.dir = data.dir;
 						this.isShowLogn = false;
-						this.isShowList = true;
 						this.loading = false;
+						
 					})
 					.catch((res) => {
-						if (res.status == 110008) {
-							this.$Message.error(res.msg);
-							this.isShowLogn = true;
-							this.isShowList = false;
-							this.loading = false;
-						} else {
-							this.loading = false;
-							this.$Message.error(res.msg);
-						}
-
+						this.catchFun(res);
 					});
 			},
+			//新建文件后重新加载左侧导航
+			getListItem(data) {
+				opendirListApi(data)
+					.then(async (res) => {
+						this.$set(this.contextData, 'children', res.data.navList);
+					})
+					.catch((res) => {
+						this.catchFun(res);
+					});
+			},
+			
 			// 返回上级
-			goBack() {
+			goBack(is_edit) {
 				this.formItem = {
 					dir: this.dir,
 					superior: 1,
 					filedir: '',
 				};
-				this.getList();
+				this.getList(false,is_edit);
 			},
 			// 打开
 			open(row) {
@@ -236,28 +267,16 @@
 					superior: 0,
 					filedir: row.filename,
 				};
-				this.getList();
+				this.getList(false,false);
 			},
 			// 编辑
 			edit(row) {
-
+				this.navItem = row;
 				this.spinShow = true;
 				this.pathname = row.pathname;
 				this.title = row.filename;
-				openfileApi(row.pathname)
-					.then(async (res) => {
-						let data = res.data;
-						this.code = res.data.content;
-						if(data.mode) this.editor.setOption(data.mode);
-						this.editor.setValue(this.code);
-						this.editor.refresh();
-						this.modals = true;
-						this.spinShow = false;
-					})
-					.catch((res) => {
-						this.spinShow = false;
-						this.$Message.error(res.msg);
-					});
+				this.navList = this.navListForTab;
+				this.openfile(row.pathname,true);
 			},
 			// 保存
 			savefile() {
@@ -268,45 +287,25 @@
 				savefileApi(data)
 					.then(async (res) => {
 						this.$Message.success(res.msg);
-						this.modals = false;
+						// this.modals = false;
 					})
 					.catch((res) => {
-						this.$Message.error(res.msg);
+						this.catchFun(res);
 					});
 			},
-			// 撤销
-			undofile() {
-				this.editor.undo();
-			},
-			redofile() {
-				this.editor.redo();
-			},
 			// 刷新
 			refreshfile() {
-				this.editor.refresh();
-			},
-
-			// 查看是否登录
-			onIsLogin() {
-				this.spinShow = true;
-				let file_login_status = window.localStorage.getItem("file_login_status"); //保存数据
-				if (file_login_status) {
-					this.getList();
-				} else {
-					this.isShowLogn = true;
-					this.spinShow = false;
-					this.isShowList = false;
-				}
-
-
+				// 刷新编辑器
+				if(!this.navItem.isDir) this.openfile(this.navItem.pathname,false);
 			},
 			// 登录跳转
 			onLogin(data) {
 				let expires = this.getExpiresTime(data.expires_time);
 				// 记录用户登陆信息
 				setCookies('file_token', data.token, expires);
-				this.getList();
+				this.getList(false,false);
 			},
+			//计算token过期时间
 			getExpiresTime(expiresTime) {
 				let nowTimeNum = Math.round(new Date() / 1000);
 				let expiresTimeNum = expiresTime - nowTimeNum;
@@ -332,75 +331,493 @@
 								this.isShowList = false;
 								this.loading = false;
 							} else {
-								this.loading = false;
-								this.$Message.error(res.msg);
+								this.catchFun(res);
 							}
 						});
 				}
 			},
 			// 自定义显示
 			renderContent (h, { root, node, data }) {
+				let that = this;
 				return h('span', {
 					style: {
 						display: "inline-block",
 						cursor: "pointer",
-						userSelect: 'none'
+						userSelect: 'null',
+						color:"#cccccc",
+						display: 'inline-block',
+						width: '100%',
+						borderRadis:'5px'
 					},
 					on: {
 						click: () => {
-						  this.clickDir(data);
+						  that.clickDir(data,root,node);
+						},
+						'contextmenu': ()=>{
+							// that.handleContextDelFolder(data,root,node);
 						}
 					}
-				},data.title);
+					
+				},[
+					h('span', [
+						h('Icon', {
+							props: {
+								type:  data.isDir ? "ios-folder-outline" : 'ios-document-outline'
+							},
+							style: {
+								marginRight: '8px'
+							}
+						}),
+						h('span',{
+							attrs:{
+								title:data.title,
+							},
+						}, data.title)
+					])
+				]);
 			},
-			clickDir(data){
+			/**
+			 * 侧边栏点击事件
+			 * @param {Object} data
+			 */
+			clickDir(data,root,node){
+				this.navItem = data;
+				this.pathname = data.pathname;
 				if(!data.isDir)
 				{
-					openfileApi(data.pathname)
-						.then(async (res) => {
-							let data = res.data;
-							this.code = res.data.content;
-							if(data.mode) this.editor.setOption(data.mode);
-							this.editor.setValue(this.code);
-							this.editor.refresh();
-						})
-						.catch((res) => {
+					this.openfile(data.pathname,false);
+				}
+			},
+			//侧边栏右键点击事件
+			handleContextMenu (data, event, position) {
+				position.left =  (Number(position.left.slice(0,-2)) + 75)+'px';
+				this.contextData = data;
+				
+			},
+			// 文件操作类型 createFolder|创建文件夹 createFile|创建文件 delFolder|删除文件夹或者文件
+			//创建文件夹
+			handleContextCreateFolder () {
+				this.formFile.filename = '';
+				this.formTitle = '创建文件夹';
+				this.formShow = true;
+				this.fileType = 'createFolder';
+				
+			},
+			//创建文件
+			handleContextCreateFile () {
+				this.formFile.filename = '';
+				this.formTitle = '创建文件';
+				this.formShow = true;
+				this.fileType = 'createFile';
+			},
+			//删除文件
+			handleContextDelFolder () {
+				let that = this;
+				that.$Modal.confirm({
+				  title: '删除文件夹和文件',
+				  content: '您确定要删除改文件?',
+				  loading: true,
+				  onOk: () => {
+					let data = {
+						path:that.contextData.pathname,
+					};
+					delFolder(data).then(async(res)=>{
+						that.loopDel(that.navList,that.contextData.nodeKey)
+						that.$Modal.remove();
+						that.$Message.success('删除成功');
+					}).catch((res)=>{
+						that.catchFun(res);
+					});
+				  },
+				  onCancel: () => {
+				    that.$Message.info('取消删除');
+				  },
+				});
+			},
+			//重命名
+			handleContextRename(){
+				this.formFile.filename = this.contextData.title;
+				this.formTitle = '重命名文件';
+				this.formShow = true;
+				this.fileType = 'renameFile';
+			},
+			//打开文件
+			openfile(path,is_edit){
+				openfileApi(path)
+					.then(async (res) => {
+						let data = res.data;
+						this.code = data.content;
+						// this.editor.setValue(this.code);
+						//改变属性
+						this.changeModel(data.mode,this.code);
+						if(is_edit)
+						{
+							this.modals = true;
 							this.spinShow = false;
-							this.$Message.error(res.msg);
-						});
+						}
+					})
+					.catch((res) => {
+						this.catchFun(res);
+					});
+			},
+			/**
+			 * 初始化编辑器
+			 */
+			initEditor(){
+				let that = this;
+				// 初始化编辑器,确保dom已经渲染
+				that.editor = monaco.editor.create(document.getElementById('container'), {
+					value:that.code,//编辑器初始显示文字
+					language:'sql',//语言支持自行查阅demo
+					automaticLayout: true,//自动布局
+					theme:'vs-dark' ,//官方自带三种主题vs, hc-black, or vs-dark
+					foldingStrategy: 'indentation', // 代码可分小段折叠
+					overviewRulerBorder: false, // 不要滚动条的边框
+					scrollbar: { // 滚动条设置
+						verticalScrollbarSize: 4, // 竖滚动条
+						horizontalScrollbarSize: 10, // 横滚动条
+					},
+					autoIndent: true, // 自动布局
+					tabSize: 4, // tab缩进长度
+					autoClosingOvertype:'always',
+				});
+
+				//添加按键监听
+				that.editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_S, function () {
+				  that.savefile();
+				});
+
+			},
+			getValue(){
+				this.editor.getValue(); //获取编辑器中的文本
+			},
+			/**
+			 * 切换语言
+			 * @param {Object} mode
+			 */
+			changeModel(mode,value){
+				var oldModel = this.editor.getModel();//获取旧模型
+				// var value = this.editor.getValue();//获取旧的文本
+				//创建新模型,value为旧文本,id为modeId,即语言(language.id)
+				//modesIds即为支持语言
+				// var modesIds = monaco.languages.getLanguages().map(function(lang) { return lang.id; });
+				if(!mode) mode = oldModel.getLanguageId();
+				// if(!value) value = this.editor.getValue();
+				
+				var newModel = monaco.editor.createModel(value,mode);
+				//将旧模型销毁
+				if(oldModel){
+					oldModel.dispose();
 				}
-			}
+				//设置新模型
+				this.editor.setModel(newModel);
+			},
+			// 文件操作类型 createFolder|创建文件夹 createFile|创建文件 delFolder|删除文件夹或者文件
+			handleSubmit(name) {
+				let that =this;
+				let data = '';
+				let dataItem = '';
+				this.$refs[name].validate((valid) => {
+					if (valid) {
+						switch (that.fileType)
+						{
+							case 'createFolder':
+								data = {
+									path:that.contextData.pathname,
+									name:that.formFile.filename
+								};
+								createFolder(data).then(async(res)=>{
+									dataItem = {
+										dir: that.contextData.path,
+										superior: 0,
+										filedir: that.contextData.title,
+									};
+									that.getListItem(dataItem)
+									if(that.formShow) that.formShow = false;
+									that.$Message.success('创建成功');
+								}).catch((res)=>{
+									that.catchFun(res);
+								});
+							break;
+							case 'createFile':
+								data = {
+									path:that.contextData.pathname,
+									name:that.formFile.filename
+								};
+								createFile(data).then(async(res)=>{
+									dataItem = {
+										dir: that.contextData.path,
+										superior: 0,
+										filedir: that.contextData.title,
+									};
+									that.getListItem(dataItem)
+									if(that.formShow) that.formShow = false;
+									that.$Message.success('创建成功');
+								}).catch((res)=>{
+									that.catchFun(res);
+								});
+							break;
+							case 'renameFile':
+								data = {
+									newname:that.contextData.path + '\\' + that.formFile.filename,
+									oldname:that.contextData.pathname
+								};
+								rename(data).then(async(res)=>{
+									that.$set(that.contextData, 'title', that.formFile.filename);
+									that.$Message.success('修改成功');
+									if(that.formShow) that.formShow = false;
+								}).catch((res)=>{
+									that.catchFun(res);
+								});
+							break;
+						}
+					} else {
+						this.$Message.error('Fail!');
+					}
+				})
+			},
+			/**
+			 * 退出表单
+			 */
+			formExit(){
+				this.formShow = false;
+			},
 			
+			/**
+			 * 处理接口回调
+			 * @param {Object} res
+			 */
+			catchFun(res){
+				if(res.status)
+				{
+					if(res.status == 400)
+					 this.$Message.error(res.msg);
+					if (res.status == 110008) {
+						this.$Message.error(res.msg);
+						this.isShowLogn = true;
+						this.isShowList = false;
+						this.loading = false;
+					}
+				}else{
+					this.$Message.error('文件编码不被兼容,无法正确读取文件!');
+				}
+				//关闭蒙版层
+				if(this.spinShow) this.spinShow = false;
+				// 关闭文件列表展示
+				if(this.loading) this.loading = false;
+			},
+			loopDel(data,nodeKey){
+				data.forEach((item, index) => {
+					if (item.nodeKey === nodeKey) {
+						return data.splice(index, 1)
+					}
+					if (item.children.length > 0) {
+						return this.loopDel(item.children, nodeKey)
+					}
+				})
+			},
+			winChanges(){
+				if(this.className)
+				{
+					this.className = '';
+				}else{
+					this.className = "diy-fullscreen";
+				}
+			}
 		},
 	};
 </script>
 <style scoped lang="stylus">
 .mt20
     >>>.ivu-icon-ios-folder-outline
-       font-size 14px !important
+       font-size 14px !important;
     >>> .ivu-icon-ios-document-outline
-       font-size 18px !important
+       font-size 18px !important;
     >>> .ivu-table-row
-       cursor pointer
+       cursor pointer;
 .mr5
    margin-right 5px
 .backs
-   cursor pointer
-   display inline-block
+   cursor pointer;
+   display inline-block;
 >>>.CodeMirror
- height: 70vh !important
+ height: 70vh !important;
  
 .file-box
-	display: flex
-	align-items: center
-	justify-content: space-between
+	display: flex;
+	align-items: flex-start;
+	justify-content: space-between;
+	position: relative;
+	height: 95%;
+	min-height: 600px;
+	overflow: hidden;
 .file-box
 	.file-left
-		width:25%
-		max-width: 400px
-		overflow: auto
-.file-box
+		position: absolute;
+		top: 53px;
+		left: 0;
+		height: 90%;
+		// height: 100%;
+		// min-height: 600px;
+		width:25%;
+		max-width: 250px;
+		overflow: auto;
+		background-color: #222222;
+		box-shadow: #000000 -6px 0 6px -6px inset;
+	.file-fix
+		flex: 1;
+		max-width: 250px;
+		height: 100%;
+		min-height: 600px;
+		// bottom: 0px;
+		// overflow: auto;
+		// min-height: 600px;
+		background-color: #222222;
+.file-box 
 	.file-content
-		flex: 75%
-		overflow: hidden
+		// position: absolute;
+		// top: 53px;
+		// left: 25%;
+		flex: 3;
+		overflow: hidden;
+		min-height: 600px;
+		height: 100%;
+>>>.ivu-modal-body
+		padding: 0;
+>>>.ivu-modal-content
+	background-color: #292929
+.diy-button
+	// float: left;
+	height: 35px;
+	line-height: 35px;
+	padding: 0 15px;
+	font-size: 13px;
+	text-align: center;
+	color: #fff;
+	border: 0;
+	border-right: 1px solid #4c4c4c;
+	cursor: pointer;
+	border-radius: 0
+	background-color: #565656
+
+.form-mask
+	z-index: -1;
+	width: 100%;
+	height: 100%;
+	position: fixed;
+	top: 0;
+	left: 0;
+	right: 0;
+	bottom: 0;
+	margin: auto;
+	background: rgba(0,0,0,0.3);
+.diy-from-header
+	height: 30px
+	line-height: 30px;
+	background-color: #fff;
+	text-align: left;
+	padding-left: 20px
+	font-size: 16px;
+	margin-bottom: 15px;
+	span
+		display: inline-block;
+		float: right;
+		color: #999;
+		text-align: right;
+		font-size: 12px;
+		width: 280px;
+		word-break:keep-all;/* 不换行 */
+		white-space:nowrap;/* 不换行 */
+		overflow:hidden;
+		text-overflow:ellipsis;
+.diy-from
+	z-index: 9999;
+	width: 400px;
+	height: 100px;
+	position: fixed;
+	top: 0;
+	left: 0;
+	right: 0;
+	bottom: 0;
+	margin: auto;
+	text-align: center;
+	background-color: #2f2f2f;
+.show-info
+	background-color: #383838;
+	color: #FFF;
+	width: 25%;
+	max-width: 250px;
+	position: absolute;
+	top: 0;
+	left: 0;
+	z-index: 1122;
+	.diy-button
+		width: 50%;
+		height: 25px;
+		line-height: 25px;
+	.diy-button-list
+		display: flex;
+		align-items: center;
+	.show-text
+		padding-left: 10px;
+		word-break:keep-all;/* 不换行 */
+		white-space:nowrap;/* 不换行 */
+		overflow:hidden;
+		text-overflow:ellipsis;
+		padding: 5px 5px;
+body >>>.ivu-select-dropdown{
+	background: #fff;
+}
+.diy-tree-render
+	>>>li
+		overflow: hidden;
+	>>>.ivu-tree-title
+		width: 90%;
+		max-width:250px;
+		padding: 0;
+		padding-left: 5px
+>>>.ivu-tree-children
+		.ivu-tree-title:hover
+			background-color:#2f2f2f !important;
+.file-box
+	.file-left::-webkit-scrollbar
+		width: 4px;
+.file-box
+	.file-left::-webkit-scrollbar-thumb
+		border-radius: 10px;
+		-webkit-box-shadow: inset 0 0 5px rgba(0,0,0,0.2);
+		background: rgba(255, 255, 255, 0.2);
+.file-box
+	.file-left::-webkit-scrollbar-track 
+		-webkit-box-shadow: inset 0 0 5px rgba(0,0,0,0.2);
+		border-radius: 0;
+		background: rgba(0,0,0,0.1);
+.diy-header
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+	.diy-header-icon
+		margin-right: 30px;
+		cursor: pointer;
+	.diy-header-icon:hover
+		opacity: 0.8;
+// 自定义方法缩小
+>>>.diy-fullscreen 
+		overflow: hidden;
+		.ivu-modal
+			top: 0px;
+			left: 0px;
+			right: 0px;
+			bottom: 0px;
+			height: 100%;
+			width: 100% !important;
+			.ivu-modal-content
+				height: 100%;
+				.ivu-modal-body
+					height: 100%;
+>>>.ivu-modal
+	.ivu-modal-content
+		.ivu-modal-body
+			height: 632px;
+			overflow: hidden;
 </style>

+ 3 - 0
template/admin/vue.config.js

@@ -7,6 +7,8 @@ const CompressionPlugin = require('compression-webpack-plugin');
 // 引入js打包工具
 const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
 
+const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
+
 const resolve = (dir) => {
   return path.join(__dirname, dir);
 };
@@ -115,6 +117,7 @@ module.exports = {
     if (Setting.isMock) {
       entry.add('@/mock').end();
     }
+    config.plugin('monaco').use(new MonacoWebpackPlugin());
   },
 
   // 设为false打包时不生成.map文件