heyujie
2021-07-10 c83e653ebaef064b0741c1f35cf6576762ccbc68
系统设置修改
7个文件已修改
9个文件已添加
4317 ■■■■■ 已修改文件
public/fonts/alibaba/iconfont.css 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/system.ts 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/cameraAccess/components/SceneRule.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/settings/index/App.vue 261 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/settings/views/NetSettings.vue 241 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/settings/views/generalSettings.vue 251 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/shuohuangMonitorAnalyze/components/searchForVideoAnalyze.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/systemSettings/components/AuthorityManagement.vue 265 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/systemSettings/components/BasicSetting.vue 1035 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/systemSettings/components/CloudNode.vue 287 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/systemSettings/components/ClusterManagement.vue 885 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/systemSettings/components/LogManagement.vue 190 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/systemSettings/components/RadioSet.vue 189 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/systemSettings/components/SystemMaintenance.vue 514 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/systemSettings/index/App.vue 161 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/systemSettings/index/main.ts 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
public/fonts/alibaba/iconfont.css
@@ -4,6 +4,12 @@
       url('iconfont.woff?t=1625566597355') format('woff'),
       url('iconfont.ttf?t=1625566597355') format('truetype');
}
@font-face {
  font-family: 'iconfont';  /* Project id 1155353 */
  src: url('//at.alicdn.com/t/font_1155353_peuhj1f227s.woff2?t=1625815343825') format('woff2'),
       url('//at.alicdn.com/t/font_1155353_peuhj1f227s.woff?t=1625815343825') format('woff'),
       url('//at.alicdn.com/t/font_1155353_peuhj1f227s.ttf?t=1625815343825') format('truetype');
}
.iconfont {
  font-family: "iconfont" !important;
src/api/system.ts
@@ -135,6 +135,21 @@
  });
};
export const upNetCard = (query: any) => {
  return request({
    url: "/data/api-v/sysset/upNetCard",
    method: "post",
    data: qs.stringify(query)
  });
};
export const downNetCard = (query: any) => {
  return request({
    url: "/data/api-v/sysset/downNetCard",
    method: "post",
    data: qs.stringify(query)
  });
};
export const setServerName = (query: any) => {
  return request({
    url: "/data/api-v/sysset/setSerName",
@@ -239,4 +254,4 @@
    method: "get",
    params: query
  });
};
};
src/pages/cameraAccess/components/SceneRule.vue
@@ -277,6 +277,7 @@
        })
        return false;
      }
      debugger
      this.eventAudio.src = this.soundPath;
      if (this.togglePlay) {
        this.eventAudio.play();
src/pages/settings/index/App.vue
@@ -8,7 +8,7 @@
        :key="index"
        @click="openMenu(item.name, index)"
      >
        <span class="icon iconfont">&#xe646;</span>
        <span class="icon iconfont">{{ item.icon }}</span>
        <span class="card-text">{{ item.name }}</span>
      </div>
    </div>
@@ -35,10 +35,18 @@
              />
            </div>
            <span class="user-name">{{ item.username }}</span>
            <el-tag
              style="margin-left: 100px"
              size="mini"
              v-if="item.id == curUserID"
              >我的</el-tag
            >
          </div>
        </div>
        <div class="add-account">
          <span class="icon iconfont" @click="showAddAccount">&#xe646;</span>
          <!-- <span class="icon iconfont" @click="openAdd">&#xe646;</span> -->
          <i class="el-icon-circle-plus" style="font-size: 38px;
" @click="openAdd"></i>
        </div>
      </div>
      <div class="datetime-left" v-if="activePage == '日期时间'">
@@ -114,8 +122,10 @@
            </div>
            <div class="user-desc">
              <div class="username">
                <span class="icon iconfont" style="margin-right: 5px"
                  >&#xe690;</span
                <span
                  class="icon iconfont"
                  style="font-size: 18px; margin-right: 3px"
                  >&#xe6de;</span
                >
                <span>{{ activeAccountItem.username }}</span>
              </div>
@@ -124,32 +134,33 @@
                <span v-show="!showInputNickName">{{
                  activeAccountItem.nickname
                }}</span>
                <!-- <input
                  type="text"
                  class="input-nick"
                  ref="input-nick"
                  v-show="showInputNickName"
                  v-model="inputNickName"
                  @blur="hideInputNick"
                  @keydown.enter="blurInputNick"
                /> -->
                <el-input
                  size="mini"
                  v-model="inputNickName"
                  v-if="showInputNickName"
                ></el-input>
                <span v-show="!showInputNickName" class="icon iconfont" @click="editNickName"
                <span
                  v-show="!showInputNickName"
                  class="icon iconfont"
                  @click="editNickName"
                  >&#xe6f0;</span
                >
                 <span v-show="showInputNickName" class="icon iconfont" @click="showInputNickName=false"
                <span
                  v-show="showInputNickName"
                  class="icon iconfont"
                  @click="showInputNickName = false"
                  >&#xe61b;</span
                >
                 <span v-show="showInputNickName" class="icon iconfont" style="font-size:21px;font-weight:600;color:green;" @click="hideInputNick"
                <span
                  v-show="showInputNickName"
                  class="icon iconfont"
                  style="font-size: 21px; font-weight: 600; color: green"
                  @click="hideInputNick"
                  >&#xe62a;</span
                >
              </div>
              <div class="user-role">
                {{ activeUserRole }}
                <el-tag type="info" size="mini">{{ activeUserRole }}</el-tag>
              </div>
            </div>
          </div>
@@ -198,7 +209,10 @@
            ref="passwordForm"
            class="password-form"
          >
            <el-form-item prop="curPassword">
            <el-form-item
              prop="curPassword"
              v-if="activeAccountItem.id == curUserID"
            >
              <div class="p-title">当前密码:</div>
              <el-input
@@ -286,14 +300,6 @@
                <span class="icon iconfont enable">&#xe62a;</span>
              </div>
            </div>
            <!-- <el-upload
              class="upload-demo"
              action="https://jsonplaceholder.typicode.com/posts/"
              :show-file-list="false"
              :http-request="uploadUserPic"
            >
              <div v-if="loadedPic == ''" class="upload-jpg-up">上传</div>
            </el-upload> -->
          </div>
          <div class="fill-group">
            <el-form
@@ -549,42 +555,13 @@
    ></generalSettings>
  </div>
  <div class="welcome-page" v-else>
    <div class="child" @click="openWelcome('账户', 0)">
    <div class="child" @click="openWelcome(item, i)" v-for="(item,i) in menuArr" :key="i">
      <div class="child-info">
        <span class="icon iconfont welcome-icon">&#xe6de;</span>
        <span class="welcome-title">账户</span>
        <span class="icon iconfont welcome-icon">{{ item.icon }}</span>
        <span class="welcome-title">{{item.name}}</span>
      </div>
    </div>
    <div class="child" @click="openWelcome('日期时间', 1)">
      <div class="child-info">
        <span class="icon iconfont welcome-icon">&#xe6ff;</span>
        <span class="welcome-title">日期时间</span>
      </div>
    </div>
    <div class="child" @click="openWelcome('集群管理', 2)">
      <div class="child-info">
        <span class="icon iconfont welcome-icon">&#xe6df;</span>
        <span class="welcome-title">集群管理</span>
      </div>
    </div>
    <div class="child" @click="openWelcome('网络设置', 3)">
      <div class="child-info">
        <span class="icon iconfont welcome-icon">&#xe6dd;</span>
        <span class="welcome-title">网络设置</span>
      </div>
    </div>
    <div class="child" @click="openWelcome('键盘和语言', 4)">
      <div class="child-info">
        <span class="icon iconfont welcome-icon">&#xe6dc;</span>
        <span class="welcome-title">键盘和语言</span>
      </div>
    </div>
    <div class="child" @click="openWelcome('通用设置', 5)">
      <div class="child-info">
        <span class="icon iconfont welcome-icon">&#xe6db;</span>
        <span class="welcome-title">通用设置</span>
      </div>
    </div>
  </div>
</template>
@@ -608,7 +585,6 @@
import netSettings from "../views/NetSettings";
import keyboardLanguage from "../views/keyboardLanguage";
import generalSettings from "../views/generalSettings";
import treeVue from "@/components/treeMenu/jsTree/tree.vue";
export default {
  name: "settings",
  components: {
@@ -683,12 +659,12 @@
      settime: "",
      weekday: "",
      menuArr: [
        { name: "账户" },
        { name: "日期时间" },
        { name: "集群管理" },
        { name: "网络设置" },
        { name: "键盘和语言" },
        { name: "通用设置" },
        { name: "账户", icon: "\ue6de" },
        { name: "日期时间", icon: "\ue6ff" },
        { name: "集群管理", icon: "\ue6df" },
        { name: "网络设置", icon: "\ue6dd" },
        { name: "键盘和语言", icon: "\ue6dc" },
        { name: "通用设置", icon: "\ue6db" },
      ],
      accountArr: [],
      jpgArr: [],
@@ -776,9 +752,7 @@
        }
      });
    },
    blurInputNick() {
      this.$refs["input-nick"].blur();
    },
    confirmChangePic() {
      updateUser({
        id: this.activeAccountItem.id,
@@ -794,7 +768,6 @@
    editNickName() {
      this.showInputNickName = true;
      this.inputNickName = this.activeAccountItem.nickname;
      this.$refs["input-nick"].focus();
    },
    hideInputNick() {
      if (this.inputNickName == this.activeAccountItem.nickname) {
@@ -805,7 +778,7 @@
        id: this.activeAccountItem.id,
        nickname: this.inputNickName,
      }).then((res) => {
        this.activeAccountItem.nickname = this.inputNickName
        this.activeAccountItem.nickname = this.inputNickName;
        this.$message.success(res.msg);
        this.fetchUserList(true);
        this.showInputNickName = false;
@@ -881,8 +854,8 @@
      });
    },
    childrenChange(item) {
      let isAllSelected = item.children.every((x) => x.selected == true);
      item.selected = isAllSelected;
      let SomeOneSelected = item.children.some((x) => x.selected == true);
      item.selected = SomeOneSelected;
    },
    plusOne(typ) {
      this.isSyncBrowser = false;
@@ -1014,14 +987,6 @@
      n = n.toString();
      return n[1] ? n : "0" + n;
    },
    // uploadUserPic(params) {
    //   let param = new FormData();
    //   param.append("file", params.file);
    //   uploadHeadPic(param).then((res) => {
    //     this.jpgArr.push(res.data);
    //     this.loadedPic = res.data;
    //   });
    // },
    initClockConf(ntpTest = false) {
      getClockInfo().then((rsp) => {
        if (rsp && rsp.success) {
@@ -1050,9 +1015,9 @@
        this.initClockConf();
      }
    },
    openWelcome(name, i) {
    openWelcome(item, i) {
      this.showWelcome = false;
      this.openMenu(name, i);
      this.openMenu(item.name, i);
    },
    showInput(typ) {
      this[`show${typ}Input`] = true;
@@ -1087,11 +1052,11 @@
        }, 1000);
      }
    },
    showAddAccount() {
    openAdd() {
      this.inAccountDetail = false;
      this.isAddAccount = true;
      this.selectedPic = 0;
      this.addForm.headpic = this.jpgArr[0];
      this.addForm.headpic = this.jpgArr[0].path;
      getRoles().then((res) => {
        if (res.success) {
          this.roleList = res.data.slice(0, 2);
@@ -1116,12 +1081,21 @@
      this.showJPGArr = false;
    },
    SaveNewPassword(formName) {
      let data;
      this.$refs[formName].validate((valid) => {
        if (valid) {
          updatePassword({
            oldPwd: this.passwordForm.curPassword,
            newPwd: this.passwordForm.newPassword,
          }).then(
          if (this.passwordForm.curPassword == "") {
            data = {
              userId: this.activeAccountItem.id,
              newPwd: this.passwordForm.newPassword,
            };
          } else {
            data = {
              oldPwd: this.passwordForm.curPassword,
              newPwd: this.passwordForm.newPassword,
            };
          }
          updatePassword(data).then(
            (res) => {
              if (res.success) {
                this.$message.success(res.msg);
@@ -1129,7 +1103,7 @@
              }
            },
            (err) => {
              this.$message.warning("保存失败," + err.msg);
              this.$message.error("保存失败," + err.msg);
            }
          );
        }
@@ -1143,6 +1117,7 @@
            password: this.addForm.password,
            nickname: this.addForm.nickName,
            headpic: this.addForm.headpic,
            roleId: this.addForm.roleId,
          };
          addUser(data).then(
            (res) => {
@@ -1150,7 +1125,7 @@
              this.fetchUserList(true);
            },
            (err) => {
              this.$message.warning("保存失败," + err.msg);
              this.$message.error("保存失败," + err.msg);
            }
          );
        } else {
@@ -1227,7 +1202,7 @@
                message: "删除成功!",
              });
            } else {
              this.$message.warning("删除失败");
              this.$message.error("删除失败");
            }
          });
        })
@@ -1275,6 +1250,10 @@
      const info = JSON.parse(sessionStorage.getItem("userInfo"));
      return info.roleName;
    },
    curUserID() {
      const info = JSON.parse(sessionStorage.getItem("userInfo"));
      return info.id;
    },
  },
};
</script>
@@ -1292,7 +1271,6 @@
    background-color: white;
    -webkit-box-flex: 0;
    -ms-flex: 0 0 33.3%;
    /* flex: 0 0 16%; */
    float: left;
    width: 250px;
    height: 200px;
@@ -1373,12 +1351,16 @@
    border-right: 5px solid rgba(248, 248, 248, 1);
    box-sizing: border-box;
    .account-left {
      .account-list{
        height: 530px;
        overflow: auto;
      }
      .add-account {
        color: rgba(61, 104, 225, 1);
        margin-top: 30px;
        .iconfont {
        margin-top: 15px;
        .el-icon-circle-plus {
          cursor: pointer;
          font-size: 32px;
          font-size: 38px;
        }
      }
      .account-card {
@@ -1469,7 +1451,6 @@
    overflow: auto;
    box-sizing: border-box;
    position: relative;
    padding: 20px 40px;
    .account-right {
      .account-content {
@@ -1533,11 +1514,11 @@
            align-items: baseline;
            min-width: 200px;
            .username {
                margin: 0 15px;
    height: 28px;
    line-height: 28px;
    text-align: left;
    font-size: 16px;
              margin: 0 15px;
              height: 28px;
              line-height: 28px;
              text-align: left;
              font-size: 16px;
              display: flex;
              align-items: center;
            }
@@ -1551,10 +1532,7 @@
                width: fit-content;
                text-align: left;
              }
              .input-nick {
                width: 80px;
                margin-right: 5px;
              }
              .iconfont {
                font-size: 14px;
                margin-left: 5px;
@@ -1571,7 +1549,7 @@
            .user-role {
              margin: 5px 0 0 15px;
              font-size: 14px;
              color: darkseagreen;
              color: skyblue;
            }
          }
        }
@@ -1598,7 +1576,6 @@
      .title {
        height: 30px;
        line-height: 30px;
        /* background-color: aliceblue; */
        margin-bottom: 10px;
        font-size: 16px;
        font-weight: 600;
@@ -1629,8 +1606,6 @@
          right: 1px;
          top: -0.5px;
          width: 45px;
          // background-color: rgba(61, 104, 225, 1);
          /* color: white; */
          border-radius: 12px;
        }
      }
@@ -1690,20 +1665,6 @@
        }
        .upload-jpg-border {
          border: 2px solid cornflowerblue;
        }
        .upload-jpg-up {
          height: 48px;
          width: 48px;
          float: left;
          display: flex;
          background-color: rgba(242, 242, 242, 1);
          border: 2px solid transparent;
          border-radius: 50%;
          justify-content: center;
          align-items: center;
          font-size: 12px;
          cursor: pointer;
        }
      }
      .add-account-page {
@@ -1770,59 +1731,7 @@
            text-decoration: underline;
          }
        }
        .ntp-bar {
          height: 40px;
          background-color: rgba(248, 248, 248, 1);
          display: flex;
          align-items: center;
          justify-content: space-between;
          padding: 0 10px;
          border-radius: 10px;
          margin-bottom: 10px;
          .title {
            min-width: 70px;
          }
          .input-area {
            width: 450px;
            height: 30px;
            background-color: rgba(240, 240, 240, 1);
            border-radius: 10px;
            line-height: 30px;
            font-size: 14px;
          }
        }
        .int-bar {
          height: 40px;
          background-color: rgba(248, 248, 248, 1);
          display: flex;
          align-items: center;
          justify-content: space-between;
          padding: 0 10px;
          border-radius: 10px;
          margin-bottom: 10px;
          .title {
            min-width: 130px;
          }
          .right {
            width: 450px;
            display: flex;
            align-items: center;
            height: 30px;
            .input-area {
              // width: 410px;
              background-color: rgba(240, 240, 240, 1);
              border-radius: 10px;
              line-height: 30px;
              width: -webkit-fill-available;
              font-size: 14px;
            }
            .test {
              width: 40px;
            }
          }
        }
      }
      .manual-time {
        .clock-wrap {
src/pages/settings/views/NetSettings.vue
@@ -27,18 +27,30 @@
            label-width="150px"
          >
            <el-form-item label="设备名称" prop="deviceName">
              <el-input v-model="ruleForm.deviceName" size="small" placeholder="必填"></el-input>
              <el-input
                v-model="ruleForm.deviceName"
                size="small"
                placeholder="必填"
              ></el-input>
            </el-form-item>
            <el-form-item label="端口" prop="port">
              <el-input v-model="ruleForm.port" placeholder="选填,外部访问的端口" size="small"></el-input>
              <el-input
                v-model="ruleForm.port"
                placeholder="选填,外部访问的端口"
                size="small"
              ></el-input>
            </el-form-item>
          </el-form>
          <div class="save-btn" @click="saveServerName">保存</div>
        </div>
        <div class="wifi" v-if="activePage == '无线网络' && !inWifiDetail">
          <switchBar :barName="`无线网卡`" @switchChange="wifiControl" :value="isOpenWifi"></switchBar>
          <switchBar
            :barName="`无线网卡`"
            @switchChange="wifiControl"
            :value="isOpenWifi"
          ></switchBar>
          <div class="wifi-option" v-for="(item, i) in wifiList" :key="i">
            <div class="name">
@@ -52,11 +64,15 @@
                class="icon iconfont"
                style="margin-left: 10px; cursor: pointer"
                @click="checkWifi(item)"
              >&#xe640;</span>
                >&#xe640;</span
              >
            </div>
          </div>
        </div>
        <div class="wifi-detail" v-if="activePage == '无线网络' && inWifiDetail">
        <div
          class="wifi-detail"
          v-if="activePage == '无线网络' && inWifiDetail"
        >
          <div class="btns">
            <div class="left">删除</div>
            <div class="right">断开连接</div>
@@ -76,14 +92,28 @@
            </el-form-item>
            <el-form-item label="密码" prop="password">
              <el-input v-model="wifiForm.password" placeholder="请输入密码" size="small" show-password></el-input>
              <el-input
                v-model="wifiForm.password"
                placeholder="请输入密码"
                size="small"
                show-password
              ></el-input>
            </el-form-item>
          </el-form>
          <switchBar :barName="`高级设置`" @switchChange="highClassSetting" :value="isHighClass"></switchBar>
          <switchBar
            :barName="`高级设置`"
            @switchChange="highClassSetting"
            :value="isHighClass"
          ></switchBar>
          <div class="title">IPV4</div>
          <el-form :model="ipv4Form" :rules="ipv4FormRules" ref="ipv4Form" label-width="150px">
          <el-form
            :model="ipv4Form"
            :rules="ipv4FormRules"
            ref="ipv4Form"
            label-width="150px"
          >
            <el-form-item label="方法">
              <el-select v-model="value" placeholder="请选择" size="small">
                <el-option
@@ -95,26 +125,46 @@
              </el-select>
            </el-form-item>
            <el-form-item label="IP" prop="ip">
              <ip-input :ip="ipv4Form.ip" @on-blur="ipv4Form.ip = arguments[0]"></ip-input>
              <ip-input
                :ip="ipv4Form.ip"
                @on-blur="ipv4Form.ip = arguments[0]"
              ></ip-input>
            </el-form-item>
            <el-form-item label="子网掩码" prop="subMask">
              <ip-input :ip="ipv4Form.subMask" @on-blur="ipv4Form.subMask = arguments[0]"></ip-input>
              <ip-input
                :ip="ipv4Form.subMask"
                @on-blur="ipv4Form.subMask = arguments[0]"
              ></ip-input>
            </el-form-item>
            <el-form-item label="网关" prop="gateway">
              <ip-input :ip="ipv4Form.gateway" @on-blur="ipv4Form.gateway = arguments[0]"></ip-input>
              <ip-input
                :ip="ipv4Form.gateway"
                @on-blur="ipv4Form.gateway = arguments[0]"
              ></ip-input>
            </el-form-item>
            <el-form-item label="首选DNS" prop="dns">
              <ip-input :ip="ipv4Form.dns1" @on-blur="ipv4Form.dns1 = arguments[0]"></ip-input>
              <ip-input
                :ip="ipv4Form.dns1"
                @on-blur="ipv4Form.dns1 = arguments[0]"
              ></ip-input>
            </el-form-item>
            <el-form-item label="备用DNS" prop="dns">
              <ip-input :ip="ipv4Form.dns2" @on-blur="ipv4Form.dns2 = arguments[0]"></ip-input>
              <ip-input
                :ip="ipv4Form.dns2"
                @on-blur="ipv4Form.dns2 = arguments[0]"
              ></ip-input>
            </el-form-item>
          </el-form>
          <div class="title">IPV6</div>
          <el-form :model="ipv6Form" :rules="ipv6FormRules" ref="ipv4Form" label-width="150px">
          <el-form
            :model="ipv6Form"
            :rules="ipv6FormRules"
            ref="ipv4Form"
            label-width="150px"
          >
            <el-form-item label="方法">
              <el-select v-model="value" placeholder="请选择" size="small">
                <el-option
@@ -126,11 +176,18 @@
              </el-select>
            </el-form-item>
            <el-form-item label="IP地址" prop="ip">
              <ip-input :ip="ipv6Form.ip" @on-blur="ipv6Form.ip = arguments[0]"></ip-input>
              <ip-input
                :ip="ipv6Form.ip"
                @on-blur="ipv6Form.ip = arguments[0]"
              ></ip-input>
            </el-form-item>
            <el-form-item label="前缀" prop="subMask">
              <div style="display: flex">
                <el-input v-model="wifiForm.password" placeholder size="small"></el-input>
                <el-input
                  v-model="wifiForm.password"
                  placeholder
                  size="small"
                ></el-input>
                <div class="ad">-</div>
                <div class="ad">+</div>
@@ -139,13 +196,22 @@
            </el-form-item>
            <el-form-item label="网关" prop="gateway">
              <ip-input :ip="ipv6Form.gateway" @on-blur="ipv6Form.gateway = arguments[0]"></ip-input>
              <ip-input
                :ip="ipv6Form.gateway"
                @on-blur="ipv6Form.gateway = arguments[0]"
              ></ip-input>
            </el-form-item>
            <el-form-item label="首选DNS" prop="dns">
              <ip-input :ip="ipv6Form.dns1" @on-blur="ipv6Form.dns1 = arguments[0]"></ip-input>
              <ip-input
                :ip="ipv6Form.dns1"
                @on-blur="ipv6Form.dns1 = arguments[0]"
              ></ip-input>
            </el-form-item>
            <el-form-item label="备用DNS" prop="dns">
              <ip-input :ip="ipv6Form.dns2" @on-blur="ipv6Form.dns2 = arguments[0]"></ip-input>
              <ip-input
                :ip="ipv6Form.dns2"
                @on-blur="ipv6Form.dns2 = arguments[0]"
              ></ip-input>
            </el-form-item>
          </el-form>
@@ -156,23 +222,37 @@
        </div>
        <div class="wire" v-if="activePage == '有线网络' && !inWireDetail">
          <div class="wire-bar" v-for="(item, i) in wireArr" :key="i" @click.self="checkWire(item)">
          <div class="title">有线网络</div>
          <div
            class="wire-bar"
            v-for="(item, i) in wireArr"
            :key="i"
            @click.self="checkWire(item)"
          >
            <div class="name">{{ "网络" + item.index }}</div>
            <div class="right">
              <span class="icon iconfont">&#xe676;</span>
              <span class="icon iconfont good" v-if="item.lower_up">&#xe6f1;</span>
              <span class="icon iconfont bad" v-else>&#xe6e6;</span>
              <el-switch
                v-model="item.active"
                active-color="rgba(61, 104, 225, 1)"
                @change="switchNetCard"
                @change="switchNetCard(item)"
              ></el-switch>
            </div>
          </div>
        </div>
        <div class="wire-detail" v-if="activePage == '有线网络' && inWireDetail">
        <div
          class="wire-detail"
          v-if="activePage == '有线网络' && inWireDetail"
        >
          <div class="title">网络设置</div>
          <el-form :model="wireForm" :rules="wireFormRules" ref="wireForm" label-width="150px">
          <el-form
            :model="wireForm"
            :rules="wireFormRules"
            ref="wireForm"
            label-width="150px"
          >
            <el-form-item label="网络名称" prop="name">
              <div class="wifi-name">{{ "网络" + activeWireItem.index }}</div>
            </el-form-item>
@@ -180,17 +260,29 @@
              <div class="wifi-name">{{ activeWireItem.name }}</div>
            </el-form-item>
            <el-form-item label="IP" prop="ip">
              <ip-input :ip="wireForm.ip" @on-blur="wireForm.ip = arguments[0]"></ip-input>
              <ip-input
                :ip="wireForm.ip"
                @on-blur="wireForm.ip = arguments[0]"
              ></ip-input>
            </el-form-item>
            <el-form-item label="子网掩码" prop="subMask">
              <ip-input :ip="wireForm.subMask" @on-blur="wireForm.subMask = arguments[0]"></ip-input>
              <ip-input
                :ip="wireForm.subMask"
                @on-blur="wireForm.subMask = arguments[0]"
              ></ip-input>
            </el-form-item>
            <el-form-item label="网关" prop="gateway">
              <ip-input :ip="wireForm.gateway" @on-blur="wireForm.gateway = arguments[0]"></ip-input>
              <ip-input
                :ip="wireForm.gateway"
                @on-blur="wireForm.gateway = arguments[0]"
              ></ip-input>
            </el-form-item>
            <el-form-item label="DNS" prop="dns">
              <ip-input :ip="wireForm.dns" @on-blur="wireForm.dns = arguments[0]"></ip-input>
              <ip-input
                :ip="wireForm.dns"
                @on-blur="wireForm.dns = arguments[0]"
              ></ip-input>
            </el-form-item>
          </el-form>
@@ -226,12 +318,10 @@
  getWireList,
  setNetWorkCard,
  getNetWorkCardInfo,
  getDevInfo,
  getDevInfo,downNetCard,upNetCard,
} from "@/api/system";
import cloudNode from "../components/CloudNode";
import ipInput from "@/components/subComponents/IPInput";
import { isIPv4 } from "@/scripts/validate";
import switchBar from "../components/switchBar";
export default {
@@ -254,7 +344,6 @@
    };
    return {
      isHighClass: false,
      ruleForm: {
        deviceName: "",
        port: "",
@@ -306,7 +395,6 @@
    };
  },
  components: {
    cloudNode,
    ipInput,
    switchBar,
  },
@@ -314,16 +402,29 @@
    this.getCurServer();
    this.fetchWireList();
  },
  beforeDestroy() { },
  beforeDestroy() {},
  props: ["barName"],
  methods: {
    // parseFlags(flags) {
    //   let arr = flags.split("|");
    //   if (arr.length) {
    //     return arr[0] == "up";
    //   }
    //   return false;
    // },
    switchNetCard(item){
      if (item.active) {
        upNetCard({
          ifname    :item.name
        }).then((res) => {
          if (res.success) {
            this.$message.success(res.data)
            this.fetchWireList();
          }
        })
      } else {
        downNetCard({ifname    :item.name}).then((res) => {
          if (res.success) {
            this.$message.success(res.data)
            this.fetchWireList();
          }
        })
      }
    },
    getCurServer() {
      getDevInfo().then((res) => {
        this.ruleForm.deviceName = res.data.server_name;
@@ -333,23 +434,11 @@
    fetchWireList() {
      getWireList().then((res) => {
        if (res && res.success) {
          this.wireArr = res.data
            .filter((x) => x.wireless == false)
            .map((itm) => {
              let arr = itm.flags.split("|");
              let f = false;
              if (arr.length) {
                f = arr[0] == "up";
              }
              return {
                flags: itm.flags,
                index: itm.index,
                mtu: itm.mtu,
                name: itm.name,
                wireless: itm.wireless,
                active: f
              }
            });
          this.wireArr = res.data;
          this.wireArr.forEach((x) => {
            let t = x.flags.split("|")[0] == "up";
            this.$set(x, "active", t);
          });
        }
      });
    },
@@ -361,10 +450,12 @@
      this.wireForm.subMask = "";
    },
    saveServerName() {
      debugger
      setServerName({
        server_name: this.ruleForm.deviceName,
        server_port: this.ruleForm.port,
      }).then((res) => {
      debugger
        console.log(res);
        if (res && res.success) {
          this.$message.success(res.data);
@@ -402,7 +493,7 @@
        }
      );
    },
    wifiControl(val) { },
    wifiControl(val) {},
    checkWifi() {
      this.inWifiDetail = true;
    },
@@ -433,6 +524,10 @@
  width: 100%;
}
.wire-detail {
  .title{
    font-size: 16px;
    margin-bottom: 10px;
  }
  .btns {
    display: flex;
    justify-content: space-between;
@@ -587,6 +682,12 @@
      }
    }
    .wire {
      .title{
        line-height: 30px;
        height: 30px;
        font-size: 16px;
        margin-bottom: 10px;
      }
      .wire-bar {
        height: 50px;
        background-color: #f8f8f8;
@@ -596,6 +697,25 @@
        box-sizing: border-box;
        padding: 0 20px;
        margin-bottom: 10px;
        border-radius:10px ;
        .name{
          font-size: 16px;
        }
        .right{
              display: flex;
    align-items: center;
      .icon{
        margin-right: 15px;
      }
          .good{
            color: #3d68e1;
            font-size: 18px;
          }
          .bad{
            color: orangered;
            font-size: 18px;
          }
        }
      }
      .wifi-name {
        height: 40px;
@@ -613,6 +733,7 @@
      border-radius: 10px;
      color: #fff;
      line-height: 40px;
      cursor: pointer;
      font-size: 14px;
      margin-top: 30px;
    }
src/pages/settings/views/generalSettings.vue
@@ -1,22 +1,20 @@
<template>
  <div class="all">
    <div class="cluster-content">
      <div class="cluster-center" ref="left">
        <div class="menu-item" @click="openRight('basic')">
          <div>设备信息</div>
        </div>
        <div class="menu-item" @click="openRight('video')">
          <div>事件录像时长</div>
        </div>
        <div class="menu-item" @click="openRight('sound')">
          <div>事件声音</div>
        </div>
        <div class="menu-item" @click="openRight('personalise')">
          <div>个性化设置</div>
    <div class="general-set">
      <div class="general-center" ref="left">
        <div
          class="menu-item"
          :class="activePage == i ? 'menu-item-active' : ''"
          @click="openRight(i)"
          v-for="(item, i) in menuArr"
          :key="i"
        >
          <span class="iconfont">{{ item.icon }}</span>
          <span class="title">{{ item.name }}</span>
        </div>
      </div>
      <div class="cluster-right">
        <div class="lang" v-if="activePage == 'basic'">
        <div class="lang" v-if="activePage == 0">
          <div class="bar">
            <span class="name">设备ID</span>
            <span class="desc">{{ deviceInfo.server_id }}</span>
@@ -58,7 +56,7 @@
            <span class="desc">{{ deviceInfo.runningTime }}</span>
          </div>
        </div>
        <div class="lang" v-if="activePage == 'video'">
        <div class="lang" v-if="activePage == 1">
          <div class="min-dur">
            <div class="title">视频截取最短时长</div>
            <div class="entity">
@@ -77,8 +75,9 @@
                controls-position="right"
                :min="0"
                :max="100"
                size="mini"
              ></el-input-number>&nbsp;s
                size="small"
              ></el-input-number
              >&nbsp;s
            </div>
          </div>
          <div class="min-dur">
@@ -99,16 +98,18 @@
                controls-position="right"
                :min="0"
                :max="100"
                size="mini"
              ></el-input-number>&nbsp;s
                size="small"
              ></el-input-number
              >&nbsp;s
            </div>
          </div>
          <div class="save-btn" @click="submitAlarm">保存</div>
        </div>
        <div class="lang" v-if="activePage == 'sound'">
        <div class="lang" v-if="activePage == 2">
          <div class="title">事件声音</div>
          <div
          <div class="bar-group">
             <div
            class="bar"
            v-for="(item, i) in soundList"
            :key="i"
@@ -116,13 +117,19 @@
            ref="soundBar"
          >
            <div class="left-part">
              <span class="icon iconfont">&#xe646;</span>
              <span class="icon iconfont" style="margin-right:12px;">&#xe6f5;</span>
              <span class="name">{{ item.name }}</span>
            </div>
            <div class="btns">
              <span class="icon iconfont">&#xe646;</span>
              <span class="icon iconfont" @click="removeSound(item)">&#xe646;</span>
              <span @click="togglePlay(item, i)">
                <i v-if="!item.isPlay" class="el-icon-video-play" style=""></i>
                <i v-else class="el-icon-video-pause"></i>
              </span>
              <span class="icon iconfont" style="cursor:pointer;" @click="removeSound(item)"
                >&#xe6cb;</span
              >
            </div>
          </div>
          </div>
          <div class="add-group">
            <el-upload
@@ -140,32 +147,46 @@
                <br />仅支持mp3/wma等格式
              </div>
            </el-upload>
            <div v-show="!showUpload"></div>
            <div v-show="!showUpload" class="upload-demo"></div>
            <div class="add-btn">
              <span class="icon iconfont" @click="showUpload = !showUpload">&#xe646;</span>
              <span class="icon iconfont" @click="showUpload = !showUpload"
                >&#xe646;</span
              >
            </div>
          </div>
        </div>
        <div class="lang" v-if="activePage == 'personalise'">
        <div class="lang" v-if="activePage == 3">
          <div class="title">图标主题</div>
          <div class="min-dur" style="cursor:pointer;" @click="selectIcons(0)">
          <div class="min-dur" style="cursor: pointer" @click="selectIcons(0)">
            <div class="title">扁平写实图标</div>
            <div class="entity">
              <div class="entity-img" v-for="(item, i) in realIconList" :key="i"></div>
              <div
                class="entity-img"
                v-for="(item, i) in realIconList"
                :key="i"
              ></div>
            </div>
          </div>
          <div class="min-dur" style="cursor:pointer;" @click="selectIcons(1)">
          <div class="min-dur" style="cursor: pointer" @click="selectIcons(1)">
            <div class="title">扁平化图标</div>
            <div class="entity">
              <div class="entity-img" v-for="(item, i) in flatIconList" :key="i"></div>
              <div
                class="entity-img"
                v-for="(item, i) in flatIconList"
                :key="i"
              ></div>
            </div>
          </div>
          <div class="title">桌面背景主题</div>
          <div class="min-dur" style="padding-top:25px;">
          <div class="min-dur" style="padding-top: 25px">
            <div class="bg-list">
              <div class="bg-img" v-for="(item, i) in tableBGList" :key="i"></div>
              <div
                class="bg-img"
                v-for="(item, i) in tableBGList"
                :key="i"
              ></div>
            </div>
          </div>
        </div>
@@ -179,21 +200,26 @@
import { uploadSound, getSoundList, deleteSound } from "@/api/event";
import config from "../../../../package.json";
import { isIPv4 } from "@/scripts/validate";
export default {
  data() {
    return {
      isHighClass: false,
      inWifiDetail: false,
      curPlayingIndex: null,
      inWireDetail: false,
      showUpload: false,
      alarmConf: {
        min_video_len: 10,
        max_video_len: 15,
      },
      menuArr: [
        { name: "设备信息", icon: "\ue933" },
        { name: "事件录像时长", icon: "\ue6f3" },
        { name: "事件声音", icon: "\ue6e1" },
        { name: "个性化设置", icon: "\ue756" },
      ],
      soundList: [],
      activePage: "basic",
      activePage: 0,
      eventAudio: new Audio(),
      deviceInfo: {
        cpu: "",
        runningTime: "",
@@ -206,6 +232,7 @@
        disks: "",
        memory: "",
      },
      isPlay: false,
      realIconList: [{}, {}, {}],
      flatIconList: [{}, {}, {}],
      tableBGList: [{}, {}, {}],
@@ -214,11 +241,35 @@
  mounted() {
    this.getDeviceInfo();
    this.getSounds();
    let _this = this;
    this.eventAudio.addEventListener("ended", () => {
      if (_this.curPlayingIndex !== null && _this.soundList.length) {
        _this.soundList[_this.curPlayingIndex].isPlay = false;
        _this.curPlayingIndex = null;
      }
    });
  },
  beforeDestroy() { },
  props: ["barName"],
  beforeDestroy() {},
  methods: {
    togglePlay(item, i) {
      debugger;
      if (this.curPlayingIndex !== null) {
        this.eventAudio.pause();
        this.soundList[this.curPlayingIndex].isPlay = false;
        if (this.curPlayingIndex === i) {
          this.curPlayingIndex = null;
          return;
        }
      }
      this.curPlayingIndex = i;
      this.eventAudio.src = "http://" + item.path;
      if (item.isPlay) {
        this.eventAudio.pause();
      } else {
        this.eventAudio.play();
      }
      item.isPlay = !item.isPlay;
    },
    getDeviceInfo() {
      getDevInfo().then((res) => {
        let info = res.data.deviceInfo;
@@ -234,8 +285,8 @@
        this.deviceInfo.disks = "( " + info.disk + ") ";
        this.deviceInfo.memory =
          (info.mem.total / 1024 / 1024 / 1024).toFixed(2) + "GB";
        this.alarmConf.min_video_len = basic.min_video_len
        this.alarmConf.max_video_len = basic.max_video_len
        this.alarmConf.min_video_len = basic.min_video_len;
        this.alarmConf.max_video_len = basic.max_video_len;
      });
    },
@@ -244,6 +295,9 @@
        .then((res) => {
          if (res.success) {
            this.soundList = res.data;
            this.soundList.forEach((x) => {
              this.$set(x, "isPlay", false);
            });
          }
        })
        .catch((e) => console.log(e));
@@ -263,43 +317,41 @@
        this.getSounds();
      });
    },
    openRight(typ) {
      this.activePage = typ;
      // if (typ == "sound") {
      //   this.getSounds();
      // }
    openRight(i) {
      this.activePage = i;
    },
    wifiControl(val) { },
    wifiControl(val) {},
    checkWifi() {
      this.inWifiDetail = true;
    },
    checkWire(item) {
      this.inWireDetail = true;
    },
    selectIcons(typ) {
    },
    selectIcons(typ) {},
    removeSound(item) {
      this.$confirm('您是否确认删除事件声音', '删除事件声音', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      this.$confirm("您是否确认删除事件声音", "删除事件声音", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      }).then(() => {
        deleteSound({
          id: item.id
        }).then((res) => {
          if (res.success) {
            this.getSounds()
            this.$message({
              type: 'success',
              message: '删除成功!'
            });
            this.showUpload = false
          id: item.id,
        }).then(
          (res) => {
            if (res.success) {
              this.getSounds();
              this.$message({
                type: "success",
                message: "删除成功!",
              });
              this.showUpload = false;
            }
          },
          (err) => {
            this.$message.error(err.msg);
          }
        }, err => {
          this.$message.error(err.msg)
        })
      })
        );
      });
    },
    clickSound(item, i) {
      this.$refs["soundBar"].forEach((x) => {
@@ -326,14 +378,14 @@
  width: 100%;
}
.cluster-content {
.general-set {
  height: 100%;
  display: flex;
  flex-direction: row;
  flex: 1;
  flex-basis: auto;
  box-sizing: border-box;
  .cluster-center {
  .general-center {
    height: 100%;
    width: 280px;
    overflow: auto;
@@ -342,18 +394,31 @@
    padding: 10px;
    border-right: 5px solid #f8f8f8;
    // background-color: lavender;
    .menu-item {
      background-color: #f8f8f8;
      height: 40px;
      height: 50px;
      margin-bottom: 10px;
      border-radius: 8px;
      line-height: 40px;
      border-radius: 10px;
      line-height: 50px;
      box-sizing: border-box;
      font-size: 14px;
      padding: 0 20px;
      cursor: pointer;
      display: flex;
      justify-content: space-between;
      .iconfont {
        margin-right: 12px;
      }
      .title {
        font-size: 16px;
      }
    }
    .menu-item-active {
      background-color: #3d68e1;
      color: white;
    }
    .menu-item:hover {
      background-color: #3d68e1;
      color: white;
    }
  }
  .cluster-right {
@@ -404,6 +469,10 @@
        text-align: left;
        margin-bottom: 5px;
      }
      .bar-group{
        height: 280px;
    overflow: auto;
      }
      .bar {
        height: 50px;
        background-color: rgba(248, 248, 248, 1);
@@ -414,7 +483,6 @@
        display: flex;
        justify-content: space-between;
        margin-bottom: 10px;
        cursor: pointer;
        .left-part {
          .icon {
            color: rgba(191, 191, 191, 1);
@@ -430,6 +498,18 @@
          display: flex;
          justify-content: space-between;
          color: rgba(191, 191, 191, 1);
          .el-icon-video-pause {
            cursor: pointer;
            font-size: 23px;
            vertical-align: middle;
            color: #409eff;
          }
          .el-icon-video-play {
            cursor: pointer;
            font-size: 23px;
            vertical-align: middle;
            color: #409eff;
          }
        }
        .desc {
          font-size: 14px;
@@ -440,12 +520,14 @@
        background-color: rgba(233, 233, 233, 1);
      }
      .add-group {
        margin-top: 50px;
        margin-top: 20px;
        display: flex;
        flex-direction: column;
        justify-content: space-between;
      }
      .upload-demo {
        height: 180px;
        transition: all .3s;
        .el-upload-dragger {
          width: 290px;
        }
@@ -454,8 +536,9 @@
        height: 40px;
        line-height: 40px;
        margin-top: 10px;
        cursor: pointer;
        .icon {
          font-size: 30px;
          font-size: 32px;
          color: rgba(61, 104, 225, 1);
        }
      }
@@ -497,16 +580,12 @@
          flex: 1;
          margin: 0 20px 0 6px;
        }
        .el-input-number--mini {
          width: 80px;
        }
        .el-input-number.is-controls-right[class*="mini"] [class*="increase"],
        .el-input-number.is-controls-right[class*="mini"] [class*="decrease"] {
          width: 23px;
        .el-input-number--small {
          width: 100px;
        }
        .el-input-number.is-controls-right .el-input__inner {
          padding-left: 13px;
          padding-right: 37px;
          padding-left: 16px;
          // padding-right: 37px;
        }
        #cut_min_duration {
          .el-slider__bar {
src/pages/shuohuangMonitorAnalyze/components/searchForVideoAnalyze.vue
@@ -426,7 +426,7 @@
      keyword: "",
      IsFollow: "",
      searchTime: [
        new Date(2020, 0, 1, 0, 0, 0),
        new Date(2021, 0, 1, 0, 0, 0),
        new Date(2021, 11, 31, 23, 59, 59),
      ],
      curTabPage: 1,
src/pages/systemSettings/components/AuthorityManagement.vue
New file
@@ -0,0 +1,265 @@
<template>
  <div class="s-authority-management">
    <div class="authority-table s-table" v-if="display">
      <el-table
        highlight-current-row
        :data="userList"
        style="width: 100%"
        :header-cell-style="{background:'#f8f8f8',color:'#222222'}"
      >
        <el-table-column align="center" type="index" label="序号" width="100"></el-table-column>
        <el-table-column :align="'center'" prop="username" label="用户名"></el-table-column>
        <el-table-column :align="'center'" prop="role" label="角色">
          <template slot-scope="scope">{{scope.row.sysRoles | roles}}</template>
        </el-table-column>
        <el-table-column label="操作" :align="'center'">
          <template slot-scope="scope">
            <el-tooltip content="编辑" placement="top" popper-class="atooltip">
              <i
                class="el-icon-edit"
                style="font-size: 18px;"
                @click="handleEdit(scope.$index, scope.row)"
              ></i>
            </el-tooltip>
            <el-tooltip content="删除" placement="top" popper-class="atooltip" v-show="false">
              <!-- :disabled="scope.row.sysRoles | roles | isSuper" -->
              <i
                class="el-icon-delete"
                style="font-size: 18px; color:red;"
                @click="handleDelete(scope.$index, scope.row)"
              ></i>
            </el-tooltip>
          </template>
        </el-table-column>
      </el-table>
    </div>
    <div class="authority-details" v-if="!display">
      <el-form label-width="80px" :rules="rules" ref="editForm" :model="editForm">
        <el-form-item label="用户名" style="width:580px" prop="username">
          <!-- <el-input v-model="editForm.username" placeholder="请输入" size="small"></el-input> -->
          <span class="m10" v-if="editForm.username == 'admin' || editForm.username == 'basic'">{{editForm.username}}</span>
          <el-input v-model="editForm.username" size="small" v-else></el-input>
        </el-form-item>
        <el-form-item label="新密码" style="width:580px" v-show="loginUser != editForm.username">
          <el-input show-password v-model="editForm.newPwd" placeholder="请输入密码" size="small"></el-input>
        </el-form-item>
        <el-form-item
          label="确认密码"
          style="width:580px"
          prop="checkPass"
          v-show="loginUser != editForm.username"
        >
          <el-input show-password v-model="editForm.checkPass" placeholder="请输入再次输入密码" size="small"></el-input>
        </el-form-item>
        <el-form-item label="权限配置" style="width:580px;">
          <!-- <el-transfer
            id="e-transfer"
            :titles="['全部角色', '当前角色']"
            v-model="editForm.roleIds"
            :props="{key: 'id', label: 'name'}"
            :data="roledata"
          ></el-transfer>-->
          <el-tree
            ref="treeMenus"
            :data="sysMenus"
            :props="props"
            node-key="id"
            :default-checked-keys="userMenus"
            show-checkbox
            check-on-click-node
            default-expand-all
            style="margin-top: 10px;"
          ></el-tree>
        </el-form-item>
        <el-form-item style="width:580px;">
          <el-button type="primary" style="float: right" @click="save" size="small">保存</el-button>
          <el-button type="info" style="margin-right: 10px;float: right" @click="goback" size="small">返回</el-button>
        </el-form-item>
      </el-form>
    </div>
  </div>
</template>
<script>
import { getUsers, getSysMenus, updataUser, getUserMenus } from "@/api/user"
export default {
  name: "AuthorityManage",
  filters: {
    roles(roles) {
      return roles.map(r => {
        return r.name
      }).join(' ')
    },
    isSuper(roles) {
      return roles.indexOf("超级管理员") >= 0
    }
  },
  data() {
    const generateData = _ => {
      const data = [];
      for (let i = 1; i <= 15; i++) {
        data.push({
          key: i,
          label: `备选项 ${i}`,
          disabled: i % 4 === 0
        });
      }
      return data;
    };
    const validateCheckPass = (rule, value, callback) => {
      if (value !== this.editForm.newPwd) {
        callback(new Error('两次输入密码不一致!'));
      } else {
        callback()
      }
    };
    var checkUserName = (rule, value, callback) => {
      if (value && value !== ''){
        let regEn = /^[A-Za-z_@.]{2,10}$/
        console.log("用户名校验!",value)
        if (!regEn.test(value)) {
          callback(new Error('请输入2位到10位字母的用户名,不能以数字开头,且不能包含汉字'))
        } else {
          callback()
        }
      } else {
        callback()
      }
    }
    return {
      display: true,
      loginUser: JSON.parse(sessionStorage.getItem('userInfo')).username,
      acknewpwd: "",
      sysMenus: [],
      userMenus: [],
      rolevalue: [],
      userList: [],
      editForm: {},
      props: {
        label: 'name'
      },
      rules: {
        username: [
          { validator: checkUserName, trigger: 'blur' }
        ],
        checkPass: [
          { validator: validateCheckPass, trigger: 'change' }
        ]
      }
    };
  },
  mounted() {
    this.fetchUserList();
    this.fetchSysMenus();
    this.initEditForm();
  },
  methods: {
    initEditForm() {
      this.editForm = {
        id: "",
        username: "",
        newPwd: "",
        checkPass: "",
        menuIds: []
      }
    },
    handleEdit(index, row) {
      this.initEditForm()
      this.display = !this.display;
      this.editForm.id = row.id;
      this.editForm.username = row.username;
      this.userMenus = []
      getUserMenus({ userId: row.id }).then(rsp => {
        if (rsp && rsp.success) {
          this.userMenus = rsp.data.menus.map(menu => {
            return menu.id
          })
        }
      })
    },
    handleDelete(index, row) {
      this.$notify({
        type: "warning",
        message: "无法删除该用户"
      })
    },
    goback() {
      this.display = !this.display;
    },
    save() {
      this.$refs.editForm.validate((valid) => {
        if (valid) {
          this.editForm.menuIds = this.$refs.treeMenus.getCheckedKeys()
          updataUser(this.editForm).then(rsp => {
            if (rsp && rsp.success) {
              this.$notify({
                type: "success",
                message: "修改成功"
              })
            }
          })
        }
      });
    },
    fetchUserList() {
      getUsers().then(rsp => {
        if (rsp && rsp.success) {
          this.userList = rsp.data;
        }
      })
    },
    fetchSysMenus() {
      getSysMenus().then(rsp => {
        if (rsp && rsp.success) {
          this.sysMenus = rsp.data;
        }
      })
    }
  }
};
</script>
<style lang="scss">
.s-authority-management {
  height: 100%;
  width: 100%;
  .authority-table,
  .authority-details {
    height: 100%;
    width: 100%;
    margin-top: 40px;
  }
  #e-transfer {
    .el-button--primary {
      color: #fff;
      background-color: #bfbfbf;
      border-color: #bfbfbf;
    }
    .el-button--primary:focus,
    .el-button--primary:hover {
      background: #4c4c4c;
      border-color: #4c4c4c;
      color: #fff;
    }
    .el-transfer-panel
      .el-transfer-panel__header
      .el-checkbox
      .el-checkbox__label {
      font-size: 14px;
    }
  }
  .el-form-item__content {
    text-align: left;
    input {
      max-width: 498px;
    }
  }
}
</style>
src/pages/systemSettings/components/BasicSetting.vue
New file
@@ -0,0 +1,1035 @@
<template>
  <div class="s-basic-setting">
    <el-tabs
      id="e-basic-setting"
      v-model="activeName"
      v-loading="loading"
      :element-loading-text="loadingText"
      type="border-card"
    >
      <!-- 本机信息 -->
      <el-tab-pane label="本机信息" name="sysInfo" v-if="isShow('settings:sysInfo')">
        <el-menu
          :default-openeds="openeds"
          background-color="#fff"
          text-color="#303133"
          active-text-color="#409EFF"
          style="height: 100%;"
          class="menu-css"
          @open="menuOpen"
          @close="menuClose"
        >
          <!-- 本机信息 -->
          <el-submenu index="0">
            <template slot="title">
              <b class="tree-font">本机信息</b>
            </template>
            <el-menu-item-group class="item-group">
              <el-form :model="sysinfo" :rules="rules" ref="sysinfo" label-width="100px">
                <el-row>
                  <el-col :span="12">
                    <el-form-item label="名称" prop="server_name">
                      <el-input v-model="sysinfo.server_name" placeholder="服务器名称" size="small"></el-input>
                    </el-form-item>
                  </el-col>
                  <el-col :span="12">
                    <el-form-item label="端口" prop="server_port">
                      <el-input v-model="sysinfo.server_port" placeholder="WEB服务端口" size="small"></el-input>
                    </el-form-item>
                  </el-col>
                </el-row>
                <el-row>
                  <el-col :span="12">
                    <el-form-item label="IP" prop="ip">
                      <ip-input :ip="sysinfo.ip" @on-blur="sysinfo.ip = arguments[0]"></ip-input>
                    </el-form-item>
                  </el-col>
                  <el-col :span="12">
                    <el-form-item label="子网掩码" prop="subMask">
                      <ip-input :ip="sysinfo.subMask" @on-blur="sysinfo.subMask = arguments[0]"></ip-input>
                    </el-form-item>
                  </el-col>
                </el-row>
                <el-row>
                  <el-col :span="12">
                    <el-form-item label="网关" prop="gateway">
                      <ip-input :ip="sysinfo.gateway" @on-blur="sysinfo.gateway = arguments[0]"></ip-input>
                    </el-form-item>
                  </el-col>
                  <el-col :span="12">
                    <el-form-item label="DNS" prop="dns">
                      <ip-input :ip="sysinfo.dns" @on-blur="sysinfo.dns = arguments[0]"></ip-input>
                    </el-form-item>
                  </el-col>
                </el-row>
                <div class="mt15 mb10 save-btn">
                  <el-button type="primary" @click="submitSysinfo" size="small">保存</el-button>
                </div>
              </el-form>
            </el-menu-item-group>
          </el-submenu>
          <el-submenu index="1">
            <template slot="title">
              <b class="tree-font">详细信息</b>
            </template>
            <el-menu-item-group class="item-group desc-info">
              <el-row :gutter="gutter">
                <el-col :span="12" class="flex-box">
                  <div class="xiangqin-label">设备ID</div>
                  <div class="xiangqing-info">{{sysinfo.server_id}}</div>
                </el-col>
                <!-- <el-col :span="12" class="flex-box">
                  <div class="xiangqin-label">设备编号</div>
                  <div class="xiangqing-info">{{sysinfo.deviceNum}}</div>
                </el-col>-->
                <el-col :span="12" class="flex-box">
                  <div class="xiangqin-label">设备型号</div>
                  <div class="xiangqing-info">{{sysinfo.deviceModel}}</div>
                </el-col>
                <el-col :span="12" class="flex-box">
                  <div class="xiangqin-label">设备类型</div>
                  <div class="xiangqing-info">{{sysinfo.deviceDesc}}</div>
                </el-col>
                <el-col :span="12" class="flex-box">
                  <div class="xiangqin-label">通道个数</div>
                  <div class="xiangqing-info">{{sysinfo.channelCount}}</div>
                </el-col>
                <el-col :span="12" class="flex-box">
                  <div class="xiangqin-label">主控版本</div>
                  <div class="xiangqing-info">{{sysinfo.masterVersion}}</div>
                </el-col>
                <el-col :span="12" class="flex-box">
                  <div class="xiangqin-label">web版本</div>
                  <div class="xiangqing-info">{{sysinfo.webVersion}}</div>
                </el-col>
                <el-col :span="12" class="flex-box">
                  <div class="xiangqin-label">硬盘信息</div>
                  <div class="xiangqing-info">{{sysinfo.disks}}</div>
                </el-col>
                <el-col :span="12" class="flex-box">
                  <div class="xiangqin-label">CPU</div>
                  <div class="xiangqing-info">{{sysinfo.cpuInfo}}</div>
                </el-col>
                <el-col :span="12" class="flex-box">
                  <div class="xiangqin-label">内存</div>
                  <div class="xiangqing-info">{{sysinfo.mem}}</div>
                </el-col>
                <el-col :span="12" class="flex-box">
                  <div class="xiangqin-label">运行时间</div>
                  <div class="xiangqing-info">{{sysinfo.uptime}}</div>
                </el-col>
              </el-row>
            </el-menu-item-group>
          </el-submenu>
          <!-- 事件录像时长 -->
          <el-submenu index="2">
            <template slot="title">
              <b class="tree-font">事件录像时长</b>
            </template>
            <el-menu-item-group class="item-group">
              <el-form
                label-width="150px"
                class="alarmSetting"
                style="padding-left:5px;padding-right:5px;"
              >
                <el-form-item label="视频截取最短时长1" style="width:724px;">
                  <el-slider
                    id="cut_min_duration"
                    v-model="alarmConf.min_video_len"
                    :step="5" show-stops :show-tooltip="true"
                  ></el-slider>
                  <el-input-number
                    v-model="alarmConf.min_video_len"
                    controls-position="right"
                    :min="5"
                    :max="120"
                    size="small"
                  ></el-input-number>&nbsp;s
                </el-form-item>
                <el-form-item label="视频截取最长时长" style="width:724px">
                  <el-slider
                    id="cut_max_duration"
                    v-model="alarmConf.max_video_len"
                 :step="5" show-stops :show-tooltip="true"
                  ></el-slider>
                  <el-input-number
                    v-model="alarmConf.max_video_len"
                    controls-position="right"
                    :min="5"
                    :max="120"
                    size="small"
                  ></el-input-number>&nbsp;s
                </el-form-item>
                <div class="mt15 mb10 save-btn">
                  <el-button type="primary" @click="submitAlarm" size="small">保存</el-button>
                </div>
              </el-form>
            </el-menu-item-group>
          </el-submenu>
          <!-- 对外服务IP 改名为外部网络(新tab)-->
        </el-menu>
      </el-tab-pane>
      <!-- 时间配置 -->
      <el-tab-pane label="时间配置" name="timeSet" v-if="isShow('settings:timeSet')">
        <el-form label-width="100px">
          <el-form-item label="设备时间">
            {{ equipmentTime }}
          </el-form-item>
          <div style="text-align: left;padding: 10px 0px">
            <div class="time-type">NTP校时</div>
            <div style="padding: 10px 0px;">
              <el-radio v-model="syncType" label="1">NTP校时</el-radio>
            </div>
          </div>
          <el-form-item label="服务器地址">
            <ip-input
              :ip="ntpServer"
              @on-blur="ntpServer= arguments[0]"
              :disabled="syncType === '2'"
            ></ip-input>
          </el-form-item>
          <el-form-item label="校时时间间隔" style="width: 41.3%;">
            <el-input-number
              v-model.number="timeInterval"
              :min="1"
              :max="60"
              placeholder="请输入"
              size="small"
              :controls="false"
              :disabled="syncType === '2'"
            ></el-input-number>&nbsp;&nbsp;&nbsp;分钟
            <el-button
              type="text"
              style="position: absolute; left: 330px;"
              :disabled="syncType === '2'"
              @click="testNTP"
              :loading="ntpTestLoading"
            >测试</el-button>
          </el-form-item>
          <div style="text-align: left;padding: 10px 0px">
            <div class="time-type">手动校时</div>
            <div style="padding: 10px 0px;">
              <el-radio v-model="syncType" label="2">手动校时</el-radio>
            </div>
          </div>
          <el-form-item label="设置时间">
            <el-date-picker
              v-model="settime"
              type="datetime"
              placeholder="选择日期时间"
              size="small"
              value-format="yyyy-MM-dd HH:mm:ss"
              :disabled="syncType === '1'"
            ></el-date-picker>
            <el-checkbox
              v-model="settimeRadio"
              style="margin-left: 12px;"
              @change="syncBrowser"
              :disabled="syncType === '1'"
            >同步本计算机时间</el-checkbox>
          </el-form-item>
          <el-col :span="12" style="padding-right: 40px;">
            <el-form-item>
              <el-button type="primary" @click="submitClock" size="small">保存</el-button>
            </el-form-item>
          </el-col>
        </el-form>
      </el-tab-pane>
      <!-- 集群管理 -->
      <el-tab-pane label="集群管理" name="cluster" v-if="isShow('settings:cluster')">
        <cluster-management></cluster-management>
      </el-tab-pane>
      <!-- <el-tab-pane label="外部访问" name="fourth">
        <el-menu
          :default-openeds="openeds"
          background-color="#fff"
          text-color="#303133"
          active-text-color="#409EFF"
          style="height: 100%;"
          class="menu-css"
          @open="menuOpen"
          @close="menuClose"
        >
          <el-submenu index="0">
            <template slot="title">
              <b class="tree-font">外部访问设置</b>
            </template>
            <el-menu-item-group class="item-group">
              <el-form :model="sysinfo" :rules="rules" ref="sysinfo" label-width="100px">
                <div class="flex-box">
                  <label>设置外部IP</label>
                  <div style="width:300px;">
                    <ip-input :ip="ipServer.ip" @on-blur="ipServer.ip = arguments[0]"></ip-input>
                  </div>
                  <el-checkbox label="选用本机IP" size="small" style="margin-left: 20px"></el-checkbox>
                </div>
                <div class="flex-box">
                  <label>域名</label>
                  <el-input size="small" v-model="ipServer.localhost"></el-input>
                </div>
                <div class="flex-box">
                  <label>本地文件端口</label>
                  <el-input size="small" v-model="ipServer.localFilePort"></el-input>
                </div>
                <div class="mt15 mb10 save-btn">
                  <el-button type="primary" @click="submitSysinfo" size="small">保存</el-button>
                </div>
              </el-form>
            </el-menu-item-group>
          </el-submenu>
        </el-menu>
      </el-tab-pane>-->
      <el-tab-pane label="权限管理" name="permission" v-if="isShow('settings:permission')">
        <authority-management v-if="activeName === 'permission'"></authority-management>
      </el-tab-pane>
      <el-tab-pane label="广播设置" name="broadcast" v-if="isShow('settings:broadcast')">
        <radio-set v-if="activeName === 'broadcast'"></radio-set>
      </el-tab-pane>
    </el-tabs>
  </div>
</template>
<script>
import {
  getDevInfo,
  getAlarmConfig,
  saveDevInfo,
  saveAlarmConfig,
  getClockInfo,
  saveClockInfo,
  testNTPserver,
  getResourceConfig,
  saveResourceConfig,
} from "@/api/system";
import { isPort, isIPv4 } from "@/scripts/validate";
import ipInput from "@/components/subComponents/IPInput";
import TimeZones from "@/Pool/TimeZones"
import ClusterManagement from "./ClusterManagement";
import AuthorityManagement from "./AuthorityManagement";
import RadioSet from "./RadioSet";
import config from '../../../../package.json'
export default {
  name: "BasicSettings",
  components: {
    ipInput,
    ClusterManagement,
    AuthorityManagement,
    RadioSet
  },
  computed: {
    // timeZoneOption() {
    //   let options = []
    //   TimeZones.forEach(zone => {
    //     options = options.concat(zone.utc.map(v => {
    //       return { value: v, label: v }
    //     }))
    //   })
    //   return options
    // },
    isAdmin() {
      if (
        sessionStorage.getItem("userInfo") &&
        sessionStorage.getItem("userInfo") !== ""
      ) {
        let loginName = JSON.parse(sessionStorage.getItem("userInfo")).username;
        return loginName === "superadmin" || loginName === "basic";
      }
      return false;
    },
  },
  directives: {
    focus: {
      inserted: function (el) {
        el.querySelector('input').focus()
      }
    }
  },
  data() {
    return {
      loading: true,
      loadingText: "",
      gutter: 10,
      activeName: "sysInfo",
      timezone: "",
      syncType: "1",
      ntpServer: "",
      equipmentTime: "",
      NYPport: "",
      settime: "",
      timeInterval: 10,
      settimeRadio: false,
      clockTimer: null,
      browserTimer: null,
      timestamp: 0,
      sysinfo: {},
      alarmConf: {},
      originNetConfig: {
        ip: "",
        gw: "",
        mask: "",
        dns: ""
      },
      rules: {
        ip: [
          {
            required: true,
            message: "请输入IP地址",
            trigger: "change"
          },
          { validator: isIPv4, trigger: "change" }
        ],
        ServerIp: [
          {
            required: true,
            message: "请输入IP地址",
            trigger: "change"
          },
          { validator: isIPv4, trigger: "change" }
        ],
        ServerPort: [
          {
            required: true,
            message: "请输入端口",
            trigger: "change"
          },
          { validator: isPort, trigger: "change" }
        ],
        GbServerPort: [
          {
            required: true,
            message: "请输入端口",
            trigger: "change"
          },
          { validator: isPort, trigger: "change" }
        ],
        gateway: [
          {
            required: true,
            message: "请输入网关",
            trigger: "change"
          },
          { validator: isIPv4, trigger: "change" }
        ],
        dns: [
          {
            required: true,
            message: "请输入dns地址",
            trigger: "change"
          },
          { validator: isIPv4, trigger: "change" }
        ],
        server_name: [
          { required: true, message: "请输入名称", trigger: "change" }
        ],
        subMask: [
          {
            required: true,
            message: "请输入子网掩码",
            trigger: "change"
          },
          { validator: isIPv4, trigger: "change" }
        ]
      },
      openeds: ["0"],
      ipServer: {
        diyOrLocalIP: "1",
        ip: "",
        localhost: "",
        localFilePort: "",
      },
      locationCity: {
        province: '',
        city: '',
        county: '',
        provinceOptions: [],
        cityOptions: [],
        countyOptions: []
      },
      webPort: 0,
      ntpTestLoading: false,
      buttonAuthority: sessionStorage.getItem("buttonAuthoritys") || [],
    };
  },
  created() {
    if (this.isShow("settings:sysInfo")) {
      this.activeName = "sysInfo";
    } else if (this.isShow("settings:timeSet")) {
      this.activeName = "timeSet";
    } else if (this.isShow("settings:cluster")) {
      this.activeName = "cluster";
    } else if (this.isShow("settings:permission")) {
      this.activeName = "permission";
    } else if (this.isShow("settings:broadcast")) {
      this.activeName = "broadcast";
    }
  },
  mounted() {
    this.$nextTick(() => {
      this.initSysinfo();
      // this.initAlarmConf();
      this.initResourceConfig();
      this.initClockConf();
      //this.markStartTime();
    });
  },
  beforeDestroy() {
    clearTimeout(this.clockTimer);
    clearInterval(this.browserTimer);
  },
  methods: {
    isShow(authority) {
      return this.isAdmin || this.buttonAuthority.indexOf("," + authority + ",") > -1
    },
    initSysinfo() {
      this.loadingText = "正在获取设备信息...";
      getDevInfo().then(rsp => {
        if (rsp && rsp.success) {
          this.sysinfo = rsp.data;
          this.sysinfo.gateway = this.sysinfo.gateway.trim();
          this.originNetConfig.ip = this.sysinfo.ip;
          this.originNetConfig.mask = this.sysinfo.subMask;
          this.originNetConfig.gw = this.sysinfo.gateway.trim();
          this.originNetConfig.dns = this.sysinfo.dns ? this.sysinfo.dns : "";
          this.alarmConf.min_video_len = rsp.data.min_video_len;
          this.alarmConf.max_video_len = rsp.data.max_video_len;
          if (this.sysinfo.deviceInfo) {
            let devInfo = this.sysinfo.deviceInfo;
            this.sysinfo.cpuInfo = devInfo.cpu[0].modelName;
            this.sysinfo.disks = "( " + devInfo.disk + ") ";
            this.sysinfo.mem = (devInfo.mem.total / 1024 / 1024 / 1024).toFixed(2) + "GB";
            // this.sysinfo.arch = devInfo.host.kernelArch;
            this.sysinfo.uptime = this.secondsFormat(devInfo.host.uptime);
          }
          if (!this.sysinfo.server_port) {
            this.sysinfo.server_port = 7003;
          }
          this.webPort = this.sysinfo.server_port;
          this.sysinfo.webVersion = 'V' + config.version;
        }
        this.loading = false;
      }).catch(err => {
        this.loading = false;
      });
    },
    secondsFormat(s) {
      var day = Math.floor(s / (24 * 3600)); // Math.floor()向下取整
      var hour = Math.floor((s - day * 24 * 3600) / 3600);
      var minute = Math.floor((s - day * 24 * 3600 - hour * 3600) / 60);
      var second = s - day * 24 * 3600 - hour * 3600 - minute * 60;
      return day + "天" + hour + "时" + minute + "分" + second + "秒";
    },
    initClockConf(ntpTest = false) {
      getClockInfo().then(rsp => {
        if (rsp && rsp.success) {
          this.timezone = rsp.data.time_zone;
          if (!ntpTest) {
            this.syncType = rsp.data.ntp ? "1" : "2";
          }
          if (rsp.data.ntp) {
            this.ntpServer = rsp.data.ntp_server;
            this.timeInterval = rsp.data.interval;
          }
          this.timestamp = rsp.data.local_time
          if (this.clockTimer === null) {
            this.runClock();
          }
        }
      })
    },
    runClock() {
      this.equipmentTime = this.formatTime(++this.timestamp, 'Y-M-D h:m:s');
      this.clockTimer = setTimeout(() => {
        this.runClock();
      }, 1000)
    },
    initAlarmConf() {
      // getAlarmConfig().then(rsp => {
      //   if (rsp && rsp.success) {
      //     this.alarmConf = rsp.data;
      //   }
      // });
    },
    initResourceConfig() {
      getResourceConfig().then(rsp => {
        if (rsp && rsp.success) {
          this.ipServer.diyOrLocalIP = rsp.data.ipType
          this.ipServer.ip = rsp.data.serviceIp
          this.ipServer.localhost = rsp.data.domain
          this.ipServer.localFilePort = rsp.data.filePort
        }
      })
    },
    syncBrowser(enable) {
      if (!enable) {
        clearInterval(this.browserTimer)
      } else {
        this.browserTimer = setInterval(() => {
          let timestamp = new Date().getTime() / 1000;
          this.settime = this.formatTime(timestamp, 'Y-M-D h:m:s')
        }, 1000)
      }
    },
    markStartTime() {
      let timestamp = new Date().getTime() / 1000;
      this.settime = this.formatTime(timestamp, 'Y-M-D h:m:s')
    },
    submitSysinfo() {
      this.$refs["sysinfo"].validate(valid => {
        if (valid) {
          if (this.sysinfo.ip !== this.originNetConfig.ip
            || this.sysinfo.subMask !== this.originNetConfig.mask
            || this.sysinfo.dns !== this.originNetConfig.dns
            || this.sysinfo.gateway !== this.originNetConfig.gw) {
            if (this.sysinfo.ip !== this.originNetConfig.ip) {
              let newUri = location.protocol + "//" + this.sysinfo.ip + ":" + this.sysinfo.server_port;
              var changeIPTimer = setTimeout(() => {
                this.$alert('<strong>您已修改了服务器ip, 请重新登录</strong><a target="_parent" href="' + newUri + '"> ' + newUri + '<a/>', '提示', {
                  dangerouslyUseHTMLString: true
                });
              }, 10000)
            }
            this.$confirm("确认需要修改服务器网络配置吗?", {
              center: true,
              cancelButtonClass: "comfirm-class-cancle",
              confirmButtonClass: "comfirm-class-sure"
            }).then(() => {
              this.loading = true;
              this.loadingText = "正在处理..."
              saveDevInfo(this.sysinfo).then(rsp => {
                if (rsp && rsp.success) {
                  this.$notify({
                    type: "success",
                    message: "本机信息保存成功"
                  });
                }
                this.initSysinfo();
                this.loading = false;
              }).catch(err => {
                this.loading = false;
                clearTimeout(changeIPTimer)
                this.$notify({
                  type: "error",
                  message: "保存失败"
                });
              });
            }).catch(err => {
            });
          } else {
            saveDevInfo(this.sysinfo).then(rsp => {
              if (rsp && rsp.success) {
                this.$notify({
                  type: "success",
                  message: "本机信息保存成功"
                });
                this.initSysinfo();
              }
            });
            if (this.sysinfo.server_port !== this.webPort) {
              let newUri = location.protocol + "//" + this.sysinfo.ip + ":" + this.sysinfo.server_port;
              var changeIPTimer = setTimeout(() => {
                this.$alert('<strong>您已修改了服务器端口, 请重新登录</strong><a target="_parent" href="' + newUri + '"> ' + newUri + '<a/>', '提示', {
                  dangerouslyUseHTMLString: true
                });
              }, 5000)
            }
          }
        } else {
          console.log("error submit!!");
          return false;
        }
      });
    },
    checkTimeZone(val) {
    },
    submitClock() {
      if (this.syncType === '1') {
        if (this.ntpServer === "") {
          this.$notify({
            type: "error",
            message: "NTP 服务器地址不能为空"
          });
          return false
        } else if (this.timeInterval === "") {
          this.timeInterval = 1;
        }
      } else {
        if (this.settime === "") {
          this.$notify({
            type: "error",
            message: "设置时间不能为空"
          });
          return false
        }
      }
      let requestBody = {
        timeZone: this.timezone,
        ntp: this.syncType === '1',
        ntpServer: this.ntpServer,
        interval: this.timeInterval,
        newTime: this.settime
      }
      saveClockInfo(requestBody).then(rsp => {
        if (rsp && rsp.success) {
          this.$notify({
            type: "success",
            message: "设置成功"
          });
        }
        this.initClockConf();
      })
    },
    testNTP() {
      this.ntpTestLoading = true;
      testNTPserver({ server: this.ntpServer }).then(rsp => {
        if (rsp && rsp.success) {
          this.$notify({
            type: "success",
            message: "时间同步成功"
          });
        } else {
          this.$notify({
            type: "error",
            message: "时间同步失败"
          });
        }
        this.ntpTestLoading = false;
        this.initClockConf(true);
      }).catch(err => {
        this.$notify({
          type: "error",
          message: "时间同步失败,请检查服务器ip"
        });
        this.ntpTestLoading = false;
      })
    },
    submitAlarm() {
      saveAlarmConfig(this.alarmConf).then(rsp => {
        if (rsp && rsp.success) {
          this.$notify({
            type: "success",
            message: "保存成功"
          });
        }
      });
    },
    submitResource() {
      let regNum = /^[0-9]*$/;
      if (!this.vaildHost(this.ipServer.localhost)) {
        this.$notify({
          type: "warning",
          message: "请输入正确格式得域名!"
        })
        return false;
      }
      if (!regNum.test(this.ipServer.localFilePort)) {
        this.$notify({
          type: 'warning',
          message: "请输入正确的端口号!"
        })
        return false;
      }
      saveResourceConfig({
        domain: this.ipServer.localhost,
        ipType: this.ipServer.diyOrLocalIP,
        serviceIp: this.ipServer.ip,
        filePort: Number(this.ipServer.localFilePort)
      }).then(res => {
        if (res && res.success) {
          this.$notify({
            type: "success",
            message: "保存成功"
          });
        } else {
          this.$notify({
            type: "error",
            message: "保存失败"
          });
        }
      })
    },
    onIpBlur(e, ip) {
      console.log(e, ip);
    },
    formatTime(number, format) {
      var formateArr = ['Y', 'M', 'D', 'h', 'm', 's'];
      var returnArr = [];
      var date = new Date(number * 1000);
      returnArr.push(date.getFullYear());
      returnArr.push(this.formatNumber(date.getMonth() + 1));
      returnArr.push(this.formatNumber(date.getDate()));
      returnArr.push(this.formatNumber(date.getHours()));
      returnArr.push(this.formatNumber(date.getMinutes()));
      returnArr.push(this.formatNumber(date.getSeconds()));
      for (var i in returnArr) {
        format = format.replace(formateArr[i], returnArr[i]);
      }
      return format;
    },
    //数据转化
    formatNumber(n) {
      n = n.toString()
      return n[1] ? n : '0' + n
    },
    menuOpen(event) {
    },
    menuClose(event) {
    },
    handleEdit(row) {
      console.log(row);
      row.edit = true;
    },
    handleCancel(row) {
      row.edit = false;
      console.log(row);
    },
    handleSave(row) {
      console.log(row);
      row.edit = false;
      this.$notify({
        message: "保存成功",
        type: "success"
      });
    },
    //校验域名
    vaildHost(str) {
      let re = /^(?=^.{3,255}$)(http(s)?:\/\/)?(www\.)?[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+(:\d+)*(\/\w+\.\w+)*$/
      return re.test(str)
    },
  }
};
</script>
<style lang="scss">
.s-basic-setting {
  width: 100%;
  height: 100%;
  .el-form {
    width: 1000px;
    // margin-left: -80px;
    .el-form-item {
      text-align: left;
      &.is-required:not(.is-no-asterisk) > .el-form-item__label:before {
        margin-left: -8px;
      }
      .el-button {
        float: right;
      }
      .el-form-item__content {
        text-align: left;
        input {
          max-width: 360px;
        }
        .el-date-editor.el-input,
        .el-date-editor.el-input__inner {
          width: 216px;
        }
        .el-checkbox__label {
          padding-left: 5px;
        }
      }
      .el-form-item__label {
        text-align: left;
      }
    }
  }
  .alarmSetting {
    .el-input {
      width: 100%;
      // padding-right: 10px;
    }
    .el-select {
      max-width: 113px;
    }
    .el-slider {
      width: calc(100% - 120px);
      display: inline-block;
      padding-right: 30px;
      box-sizing: border-box;
      vertical-align: middle;
    }
    .el-input-number {
      width: 100px;
      display: inline-block;
      .el-input {
        width: 100%;
      }
    }
  }
  .time-type {
    height: 25px;
    width: 413px;
    line-height: 28px;
    padding: 3px 23px;
    font-size: 14px;
    font-weight: 600;
    background-color: #e4e6ed;
  }
  #cut_min_duration {
    .el-slider__bar {
      background-color: #3d68e1;
    }
    .el-slider__button {
      width: 10px;
      height: 10px;
      border: 4px solid #3d68e1;
    }
  }
  #cut_max_duration {
    .el-slider__bar {
      background-color: #ff9e6e;
    }
    .el-slider__button {
      width: 10px;
      height: 10px;
      border: 4px solid #ff9e6e;
    }
  }
  .menu-css,
  .el-menu {
    border-right: none;
    list-style: none;
    position: relative;
    margin: 0;
    padding-left: 10px;
    background-color: #ffffff;
    .el-submenu__title {
      height: 35px;
      line-height: 35px;
      font-size: 14px;
      color: #303133;
      padding: 0 20px;
      list-style: none;
      cursor: pointer;
      position: relative;
      -webkit-transition: border-color 0.3s, background-color 0.3s, color 0.3s;
      transition: border-color 0.3s, background-color 0.3s, color 0.3s;
      -webkit-box-sizing: border-box;
      box-sizing: border-box;
      white-space: nowrap;
    }
    .tree-font {
      font-family: PingFangSC-Medium;
      font-size: 14px;
      color: #222222;
      text-align: left;
    }
    li {
      text-align: left;
      .el-submenu__title {
        // border-bottom: solid 1px #e6e6e6;
        padding-left: 10px !important;
        background-color: #e4e6ed !important;
        border-radius: 2px;
        .el-submenu__icon-arrow {
          position: absolute;
          top: 50%;
          right: auto;
          left: 135px;
          margin-top: -7px;
          -webkit-transition: -webkit-transform 0.3s;
          transition: -webkit-transform 0.3s;
          transition: transform 0.3s;
          transition: transform 0.3s, -webkit-transform 0.3s;
          font-size: 12px;
        }
      }
    }
  }
  .save-btn {
    text-align: right;
    position: relative;
    right: 40px;
  }
}
</style>
<style lang="scss" scoped>
.flex-box {
  display: flex;
  height: 50px;
  label {
    width: 120px;
  }
  .el-input {
    width: 300px;
  }
}
.desc-info {
  margin-bottom: 14px;
  .flex-box {
    height: 40px;
    line-height: 40px;
    .xiangqin-label {
      width: 80px;
    }
  }
}
.menu-css,
.el-menu {
  border-right: none;
  list-style: none;
  position: relative;
  margin: 0;
  padding-left: 0;
  background-color: #ffffff;
  .tree-font {
    font-family: PingFangSC-Medium;
    font-size: 14px;
    color: #222222;
    text-align: left;
  }
  li {
    text-align: left;
    .el-submenu__title {
      .el-submenu__icon-arrow {
        position: absolute;
        top: 50%;
        right: 0;
        margin-top: -7px;
        -webkit-transition: -webkit-transform 0.3s;
        transition: -webkit-transform 0.3s;
        transition: transform 0.3s;
        transition: transform 0.3s, -webkit-transform 0.3s;
        font-size: 12px;
      }
    }
  }
}
</style>
src/pages/systemSettings/components/CloudNode.vue
New file
@@ -0,0 +1,287 @@
<template>
  <div class="cloud">
    <div class="inner">
      <!-- <div
        class="rect"
        :style="{width:`${minWidth+BaseWidth}px`,height:`${minHeight+BaseHeight}px`}"
      >-->
      <div class="rect">
        <!-- <div
          class="node"
          v-for="item in insideNodes"
          :key="item.id"
          :style="{top:item.t+'px',left:item.l+'px'}"
        >
          <span class="node-icon">
          </span>
          <span class="node-name">{{item.nodeName}}</span>
        </div>-->
        <serfDiagram
          ref="inside-nodes"
          :members="insideNodes"
          :agent="agentName"
          :sizeX="insideSizeX"
          :sizeY="insideSizeY"
          :startX="insideStartX"
          :isShowHover="true"
          class="inside-nodes"
        ></serfDiagram>
      </div>
    </div>
    <div class="outer" v-if="outsideNodes.length">
      <serfDiagram
        ref="outer-nodes"
        :members="outsideNodes"
        :agent="agentName"
        :sizeX="280"
        :sizeY="370"
        :startX="60"
        class="outer-nodes"
      ></serfDiagram>
      <!-- <div
        class="node"
        v-for="(item,index) in outsideNodes"
        :key="item.id"
        :style="{top:36*(index+1)+'px',left:60*(index+1)+'px'}"
      >
        <span class="node-icon"></span>
        <span class="node-name">{{item.nodeName}}</span>
      </div>-->
    </div>
  </div>
</template>
<script>
import SerfDiagram from "@/components/serfDiagram";
export default {
  name: 'cloudNode',
  props: {
    nodes: Array
  },
  components: {
    SerfDiagram
  },
  data () {
    return {
      agentName: '',
      nodeIcons: [],
      //insideNodes: [],
      BaseWidth: 150,
      BaseHeight: 70,
      minWidth: 0,
      minHeight: 0,
      mockData: [
        {
          cluster_id: "b6132bfe-d3af-4710-ba89-436f614c2f46",
          hardwareType: "01",
          id: "DSVAD010120190622",
          node_id: "DSVAD010120190622",
          node_ip: "192.168.20.10:30190",
          nodeName: "开发20.10-1",
          role: 'pc'
        },
        {
          cluster_id: "b6132bfe-d3af-4710-ba89-436f614c2f",
          hardwareType: "02",
          id: "DSVAD010120190623",
          node_id: "DSVAD010120190623",
          node_ip: "192.168.20.10:30190",
          nodeName: "开发测试20.11-1",
          role: 'master'
        },
        {
          cluster_id: "b6132bfe-d3af-4710-ba89-436f614c2g",
          hardwareType: "03",
          id: "DSVAD010120190624",
          node_id: "DSVAD010120190624",
          node_ip: "192.168.20.10:30190",
          nodeName: "测试20.13-1",
          role: 'pc'
        },
        {
          cluster_id: "b6132bfe-d3af-4710-ba89-436f614c2h",
          hardwareType: "03",
          id: "DSVAD010120190625",
          node_id: "DSVAD010120190625",
          node_ip: "192.168.20.10:30190",
          nodeName: "测试20.101-1",
          role: 'server'
        },
        {
          cluster_id: "b6132bfe-d3af-4710-ba89-436f614cf",
          hardwareType: "02",
          id: "DSVAD01012019063",
          node_id: "DSVAD01012019063",
          node_ip: "192.168.20.10:30190",
          nodeName: "开发测试20.15-1",
          role: 'master'
        },
        {
          cluster_id: "b6132bfe-d3af-710-ba89-436f614cf",
          hardwareType: "02",
          id: "DSVAD0101201906",
          node_id: "DSVAD0101209063",
          node_ip: "192.168.20.10:30190",
          nodeName: "k20.15-128437586",
          role: 'master'
        },
        {
          cluster_id: "b612bfe-d3af-710-ba89-436f614cf",
          hardwareType: "02",
          id: "DSVAD010120906",
          node_id: "DSVAD010120963",
          node_ip: "192.168.20.107:30190",
          nodeName: "kfl20.15-127586",
          role: 'master'
        },
        {
          cluster_id: "b6132bfe-d3af-710-ba89-436f614cf",
          hardwareType: "02",
          id: "DSVAD0101201906",
          node_id: "DSVAD001209063",
          node_ip: "192.168.20.10:30190",
          nodeName: "k20.15-128437586",
          role: 'master'
        },
        {
          cluster_id: "b612bfe-d3af-710-ba89-436f614cf",
          hardwareType: "02",
          id: "DSVAD01010906",
          node_id: "DSVAD012063",
          node_ip: "192.168.20.107:30190",
          nodeName: "kfl20.15-127586",
          role: 'master'
        },
        {
          cluster_id: "b6132bfe-d3af-710-ba89-436f614cf",
          hardwareType: "02",
          id: "DSVAD0101201906",
          node_id: "DSVAD0012063",
          node_ip: "192.168.20.10:30190",
          nodeName: "k20.15-128437586",
          role: 'master'
        },
        {
          cluster_id: "b612bfe-d3af-710-ba89-436f614cf",
          hardwareType: "02",
          id: "DSVAD01010906",
          node_id: "DSVAD01012063",
          node_ip: "192.168.20.107:30190",
          nodeName: "kfl20.15-127586",
          role: 'master'
        },
      ]
    }
  },
  mounted () {
    console.log(this.nodes)
    //this.getInsideNodes();
  },
  methods: {
    getRandom (index) {
      if (index % 2 == 0) {
        return Math.random() * 20
      } else {
        return Math.random() * 50
      }
    },
    getInsideNodes () {
      //return this.nodes.filter(item=>item.hardwareType=='01'||item.hardwareType=='02');
      //return this.mockData.filter(item => item.hardwareType == '01' || item.hardwareType == '02');
      let arr = this.nodes.filter(item => item.hardwareType == '01' || item.hardwareType == '02');
      let len = arr.length;
      let lefts = [];
      let tops = [];
      this.insideNodes = arr.map((item, index) => {
        lefts.push((20 - len) * (index + 1) + this.getRandom(index));
        tops.push(30 * (index + 1));
        return {
          l: 10 + this.getRandom(index),
          t: 30 * (index + 1),
          nodeName: item.nodeName,
          id: item.id,
          workType: item.workType
        }
      });
      this.minWidth = Math.max(...lefts) - Math.min(...lefts);
      this.minHeight = Math.max(...tops) - Math.min(...tops);
      console.log('w,h', this.minWidth, this.minHeight);
    },
  },
  computed: {
    cloudPic () {
      return '/images/settings/cloud.png'
    },
    insideNodes () {
      return this.nodes.filter(item => item.hardwareType == '01' || item.hardwareType == '02');
    },
    insideSizeX () {
      return 160 + 200 * 0.2 * (this.insideNodes.length) <= 400 ? 160 + 200 * 0.2 * (this.insideNodes.length) : 400;
    },
    insideSizeY () {
      return 140 + 200 * 0.2 * (this.insideNodes.length) <= 380 ? 140 + 200 * 0.2 * (this.insideNodes.length) : 380;
    },
    insideStartX () {
      return this.insideSizeX / 3
    },
    // insideNodes () {
    //   //return this.nodes.filter(item=>item.hardwareType=='01'||item.hardwareType=='02');
    //   //return this.mockData.filter(item => item.hardwareType == '01' || item.hardwareType == '02');
    //   let arr = this.mockData.filter(item => item.hardwareType == '01' || item.hardwareType == '02');
    //   let len = arr.length;
    //   let lefts = [];
    //   let tops = [];
    //   let temp = arr.map((item,index)=>{
    //     lefts.push((20-len)*(index+1));
    //     tops.push(30*(index+1));
    //     return {
    //       l: (20-len)*(index+1),
    //       t: 30*(index+1),
    //       nodeName: item.nodeName,
    //       id: item.id,
    //       workType: item.workType
    //     }
    //   });
    //   return temp;
    // },
    outsideNodes () {
      return this.nodes.filter(item=>item.hardwareType=='03');
      //return this.mockData.filter(item => item.hardwareType == '03');
    }
  }
}
</script>
<style lang="scss">
.cloud {
  width: 100%;
  display: flex;
  .inner {
    background: url('/images/settings/easy-cloud.png') no-repeat;
    background-size: 100%;
    margin-top: -80px;
    .rect {
      //background: rgba(176, 203, 253, 0.3);
      position: relative;
      margin: 130px 100px 100px;
      .node {
        position: absolute;
        .node-icon {
          width: 40px;
          height: 40px;
        }
        .node-name {
          font-size: 14px;
          color: #333;
        }
      }
    }
  }
  .outer {
    width: 40%;
    position: relative;
    text-align: left;
    .node {
      position: absolute;
    }
  }
}
</style>
src/pages/systemSettings/components/ClusterManagement.vue
New file
@@ -0,0 +1,885 @@
<template>
  <div class="s-cluster-management">
    <div class="ui-top-view">
      <div class="ui-top-title">视频分析集群管理</div>
    </div>
    <el-row>
      <el-col :span="12">
        <el-tabs v-model="activeName" id="e-alaycluster" v-if="!isHasColony">
          <el-tab-pane label="创建集群" name="1" :disabled="isHasColony">
            <el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px">
              <el-form-item label="集群名称" prop="clustername">
                <el-input v-model="ruleForm.clustername" placeholder="手动输入, 如“集群A”" size="small"></el-input>
              </el-form-item>
              <el-form-item label="集群ID">
                <el-input v-model="clusterid" placeholder="不允许输入,保存后回显" disabled size="small"></el-input>
              </el-form-item>
              <el-form-item label="集群密码" prop="clusterpwd">
                <el-input v-model="ruleForm.clusterpwd" placeholder="请输入6位密码,或点击生成" size="small">
                  <el-button type="text" slot="suffix" @click="generatePassword">生成密码</el-button>
                </el-input>
              </el-form-item>
              <el-form-item label="集群IP" prop="virtualIp">
                <ip-input :ip="ruleForm.virtualIp" :on-blur="onIpBlur" class="ip-input-comp"></ip-input>
              </el-form-item>
              <el-form-item>
                <el-button type="primary" size="small" @click="submitForm('ruleForm')">保存</el-button>
              </el-form-item>
            </el-form>
          </el-tab-pane>
          <!-- 加入已有集群 -->
          <el-tab-pane label="加入已有集群" name="2" :disabled="isHasColony">
            <el-form
              label-width="80px"
              :model="joinForm"
              :rules="joinRules"
              ref="joinForm"
              class="join-form"
              v-loading="joinLoading"
            >
              <el-form-item label="集群ID" style="width:440px" prop="clusterid">
                <el-input v-model="joinForm.clusterid" placeholder size="small"></el-input>
              </el-form-item>
              <el-form-item label="IP地址" style="width:440px" prop="clusterip">
                <el-input
                  v-model="joinForm.clusterip"
                  placeholder="请输入集群内任意IP地址"
                  size="small"
                  autocomplete="new-password"
                ></el-input>
                <!-- <el-button size="mini" :disabled="searchDis" @click="searchColony">搜索集群</el-button>
                <el-button size="mini" @click="stopSearch">停止搜索</el-button>-->
              </el-form-item>
              <el-form-item label="集群密码" prop="clusterpwd" style="width:440px">
                <el-input
                  v-model="joinForm.clusterpwd"
                  placeholder="请输入集群密码"
                  show-password
                  size="small"
                  autocomplete="new-password"
                >
                  <el-button
                    type="text"
                    slot="suffix"
                    v-show="!searchDis"
                    @click="searchColony"
                  >搜索集群</el-button>
                  <el-button type="text" slot="suffix" v-show="searchDis" @click="stopSearch">
                    <i class="el-icon-loading"></i>停止搜索
                  </el-button>
                </el-input>
              </el-form-item>
              <el-form-item style="width:440px">
                <el-button type="primary" @click="join('joinForm')" size="small">加入集群</el-button>
              </el-form-item>
              <div class="form-tip">请输入以上信息加入集群,或者通过集群密码搜索后进行加入</div>
            </el-form>
          </el-tab-pane>
        </el-tabs>
        <!-- 有集群的情况 -->
        <div v-if="isHasColony" id="h-alaycluster">
          <el-form :model="ruleForm" ref="ruleForm" label-width="100px">
            <el-form-item label="集群名称" prop="clustername">
              <el-input v-model="ruleForm.clustername" placeholder="手动输入, 如“集群A”" size="small"></el-input>
            </el-form-item>
            <el-form-item label="集群ID">
              <el-input v-model="clusterid" placeholder="不允许输入,保存后回显" disabled size="small"></el-input>
            </el-form-item>
            <el-form-item label="集群密码" prop="clusterpwd">
              <el-input
                v-model="ruleForm.clusterpwd"
                disabled
                placeholder="请输入6位密码,或点击生成"
                size="small"
              ></el-input>
            </el-form-item>
            <el-form-item label="集群IP" prop="virtualIp">
              <ip-input :ip="ruleForm.virtualIp" :on-blur="onIpBlur" class="ip-input-comp"></ip-input>
            </el-form-item>
            <el-form-item style="text-align: right;">
              <el-button size="small" type="danger" @click="leave">退出集群</el-button>
              <el-button
                style="margin-right:10px;"
                type="primary"
                size="small"
                @click="updateCluster('manageForm')"
              >保存</el-button>
            </el-form-item>
          </el-form>
        </div>
      </el-col>
      <el-col
        :span="12"
        style="height: 100%;"
        v-if="members.length !== 0 && isSearch"
        class="node-container"
      >
        <serfDiagram
          ref="diagram"
          :members="members"
          :agent="agentName"
          v-loading="loading"
          :isShowHover="false"
          @selected-node="joinNode"
          class="nodes-svg"
        ></serfDiagram>
      </el-col>
      <el-col
        :span="12"
        style="height: 100%;"
        v-if="innerNodes.length !== 0 && !isSearch"
        class="node-container"
      >
        <cloud-node :nodes="innerNodes"></cloud-node>
      </el-col>
    </el-row>
    <!-- <div class="ui-top-view">
      <div class="ui-top-title">存储集群管理</div>
    </div>
    <el-row>
      <el-col :span="12">
        <el-tabs id="e-dbcluster" v-model="sActiveName">
          <el-tab-pane label="创建集群" name="s-first" v-if="sActiveName != 's-third1'">
            <el-button
              type="primary"
              style="float: left;margin: 20px 0px;"
              size="small"
              @click="createEsCluster()"
            >创建存储集群</el-button>
          </el-tab-pane>
          <el-tab-pane label="加入已有集群" name="s-second" v-if="sActiveName != 's-third1'">
            <el-form label-width="80px">
              <el-form-item label="IP地址" style="text-align: left;width: 300px;">
                <el-input
                  v-model="esNodeIp"
                  placeholder="请输入集群内任意IP地址"
                  size="small"
                  autocomplete="off"
                ></el-input>
                <el-checkbox
                  label="主节点"
                  v-model="esNodeType"
                  style="margin-left: 20px;position: absolute;"
                ></el-checkbox>
              </el-form-item>
              <el-form-item>
                <el-button
                  type="primary"
                  @click="joinESCluster()"
                  size="small"
                  style="float: left;"
                >加入集群</el-button>
              </el-form-item>
            </el-form>
          </el-tab-pane>
          <el-tab-pane label="集群信息" name="s-third">
            <el-table :data="esNodes" style="width: 100%">
              <el-table-column prop="nodeType" label="节点类型"></el-table-column>
              <el-table-column prop="name" label="节点名称"></el-table-column>
              <el-table-column prop="ip" label="节点IP地址" min-width="90px"></el-table-column>
              <el-table-column prop="buildDate" label="注册时间" min-width="120px"></el-table-column>
            </el-table>
          </el-tab-pane>
        </el-tabs>
      </el-col>
    </el-row>-->
  </div>
</template>
<script>
import {
  createSerfCluster,
  randomPwd,
  search,
  getSearchNodes,
  stopSearching,
  findCluster,
  updateClusterName,
  joinCluster,
  leave,
  getVrrp,
  setVrrp,
  createESNode,
  addESNode,
  getEsClusterInfo
} from "@/api/clusterManage";
import {
  getDevInfo
} from "@/api/system";
import cloudNode from "./CloudNode";
import serfDiagram from "@/components/serfDiagram";
import ipInput from "@/components/subComponents/IPInput";
import { isIPv4 } from "@/scripts/validate";
export default {
  components: {
    serfDiagram,
    ipInput,
    cloudNode
  },
  data() {
    const checkPwd = (rule, value, callback) => {
      if (!value) {
        return callback(new Error("密码不能为空"));
      }
      setTimeout(() => {
        if (value.length != 6) {
          callback(new Error("密码应为6位!"));
        } else {
          callback();
        }
      }, 1000);
    };
    return {
      activeName: "1",
      sActiveName: "s-first",
      clusterid: "",
      esNodeIp: "",
      esNodeType: "",
      clusterpwd2: "",
      sClusterip: "",
      ruleForm: {
        clustername: "",
        clusterpwd: "",
        virtualIp: ""
      },
      vrIpForm: {
        enable: true,
        //serve_port: "",
        serve_port: null,
        virtual_ip: ""
      },
      manageForm: {
        clustername: "测试集群1",
        clusterpwd: "123456",
        virtualip: "192.168.1.188"
      },
      joinForm: {
        clusterid: "",
        clusterip: "",
        clusterpwd: ""
      },
      rules: {
        clustername: [
          { required: true, message: "请输入集群名称", trigger: "change" }
        ],
        clusterpwd: [{ validator: checkPwd, trigger: "change" }],
        virtualIp: [
          { required: true, validator: isIPv4, trigger: "change" }
        ]
      },
      // vrIpRules: {
      //   virtualIp: [
      //     { required: true, message: "请输入虚拟IP", trigger: "change" }
      //   ]
      // },
      joinRules: {
        clusterid: [
          { required: true, message: "请输入集群ID", trigger: "change" },
        ],
        clusterip: [
          { required: true, validator: isIPv4, trigger: "change" }
        ],
        clusterpwd: [
          { validator: checkPwd, trigger: "change" }
        ]
      },
      esNodes: [],
      scheduleId: "",
      isHasColony: false,
      isSearch: false,
      currentCluster: {},
      searchNum: "",
      loading: false,
      searchDis: false,
      agentName: "",
      members: [],
      innerNodes: [],
      intervalTimer: null,
      joinLoading: false,
      showJoinConfirm: false
    };
  },
  mounted() {
    this.findCluster();
    let _this = this;
    this.intervalTimer = setInterval(() => {
      _this.findCluster();
    }, 30000);
    //this.getEsClusterNodes();
  },
  beforeDestroy() {
    clearInterval(this.intervalTimer);
  },
  methods: {
    checkPsd(psd) {
      return psd.trim().length === 6
    },
    // cleanValue() {
    //   this.members = [];
    // },
    // sHandleClick(tab, event) {
    //   console.log(tab, event);
    // },
    submitForm(formName) {
      this.$refs[formName].validate(valid => {
        if (valid) {
          //alert("submit!");
          let json = {
            clusterId: this.clusterid,
            clusterName: this.ruleForm.clustername,
            password: this.ruleForm.clusterpwd,
            virtualIp: this.ruleForm.virtualIp
          };
          this.createCluster(json).then(() => {
            this.findCluster();
          });
        } else {
          console.log("error submit!!");
          return false;
        }
      });
    },
    // saveForm(formName) {
    //   this.$refs[formName].validate(valid => {
    //     if (valid) {
    //       alert("submit!");
    //       let json = {
    //         enable: this.vrIpForm.enable,
    //         serve_port: this.vrIpForm.serve_port,
    //         virtual_ip: this.vrIpForm.virtual_ip,
    //       };
    //       setVrrp(json).then(() => {
    //         this.getVrrpInfo();
    //       });
    //     } else {
    //       console.log("error submit!!");
    //       return false;
    //     }
    //   });
    // },
    join(formName) {
      let _this = this;
      this.$refs[formName].validate(valid => {
        if (valid) {
          _this.joinLoading = true;
          // if (Object.keys(this.currentCluster).length === 0) {
          //   this.$notify({
          //     type: "info",
          //     duration: 1000,
          //     message: "请先选择一个集群节点"
          //   });
          //   _this.joinLoading = false;
          //   return true;
          // }
          let nodeIps = this.members.map(i => {
            return i.Address;
          });
          let json = {
            //clusterId: this.currentCluster.cluster_id,
            clusterId: this.joinForm.clusterid,
            password: this.joinForm.clusterpwd,
            nodeIps: [this.joinForm.clusterip]
          };
          this.joinCluster(json).then(() => {
            _this.joinLoading = false;
            this.findCluster();
          }).catch(e => {
            console.log(e);
            _this.joinLoading = false;
          });
        } else {
          console.log("error submit!!");
          return false;
        }
      });
    },
    async createCluster(json) {
      let res = await createSerfCluster(json);
      console.log(res, "创建集群");
      this.$notify({
        title: res.success ? "成功" : "失败",
        message: res.msg,
        type: res.success ? "success" : "error"
      });
    },
    async randomPwd() {
      let res = await randomPwd();
      if (res && res.success) {
        this.ruleForm.clusterpwd = res.data;
      }
    },
    async searchColony() {
      this.isSearch = true;
      this.$refs["joinForm"].clearValidate();
      this.$refs["joinForm"].validateField('clusterpwd');
      if (this.checkPsd(this.joinForm.clusterpwd)) {
        this.members = [];
        let json = {
          password: this.joinForm.clusterpwd,
          //ip: this.joinForm.clusterip
        };
        this.search(json)
          .then(() => {
            this.setSchedule();
            // this.searchDis = false;
            // this.loading = false;
          })
          .catch(() => {
            this.searchDis = false;
            this.loading = false;
            this.isSearch = false;
          });
      } else {
        this.searchDis = false;
        this.loading = false;
        this.isSearch = false;
        return false;
      }
      //});
    },
    async search(json) {
      let res = await search(json);
      if (res && res.success) {
        console.log(res, "搜索集群");
        this.searchNum = res.data;
      }
      this.searchDis = true;
      this.loading = true;
      window.setTimeout(() => {
        this.stopSearch();
      }, 10 * 1000);
    },
    //搜索集群
    async getSearchNodes() {
      let res = await getSearchNodes();
      if (res && res.success) {
        let list = res.data.map(i => {
          let obj = {};
          obj.cluster_id = i.clusterID ? i.clusterID : "";
          obj.create_time = i.create_time ? i.create_time : "";
          obj.id = i.nodeID ? i.nodeID : "";
          obj.node_id = i.nodeID ? i.nodeID : "";
          obj.Address = i.nodeAddress ? i.nodeAddress : "";
          obj.nodeName = i.nodeAddress ? i.nodeAddress : "";
          obj.role = i.role ? i.role : "pc";
          return obj;
        });
        list.map(i => {
          let found = this.members.find(element => {
            return element.node_id === i.node_id;
          });
          if (found === undefined) {
            this.members.push(i);
          }
        });
      }
    },
    setSchedule() {
      this.scheduleId = window.setInterval(() => {
        this.getSearchNodes();
      }, 1000);
    },
    async stopSearch() {
      if (!this.loading) {
        return true;
      }
      stopSearching({
        searchNum: this.searchNum
      }).then((res) => {
        console.log(res, '正常结束')
        this.loading = false;
        this.searchDis = false;
        window.clearInterval(this.scheduleId);
      }).catch((err) => {
        console.log(err, '报错结束')
        this.$notify({
          type: 'error',
          duration: 1000,
          message: '停止搜索报错!'
        })
        // window.setTimeout(()=>{
        //   this.loading = false;
        //   this.searchDis = false;
        //   window.clearInterval(this.scheduleId);
        // },2000)
      })
    },
    async findCluster() {
      let res = await findCluster();
      if (res && res.success) {
        if (res.data && res.data.clusterId) {
          this.isHasColony = true;
          this.activeName = "3";
          this.clusterid = res.data.clusterId;
          this.ruleForm.clustername = res.data.clusterName;
          this.ruleForm.clusterpwd = '******';
          this.ruleForm.virtualIp = res.data.virtualIp
          //let list = res.data.nodes.map(i => {
          this.isSearch = false;
          this.innerNodes = res.data.nodes.map(i => {
            let obj = {};
            obj.device_type = i.device_type;
            obj.workType = i.device_type.substr(2, 2);
            obj.hardwareType = i.device_type.substr(4, 2);
            obj.cluster_id = i.cluster_id;
            obj.clusterName = res.data.clusterName;
            obj.create_time = i.create_time;
            obj.id = i.id;
            obj.node_id = i.node_id;
            obj.node_ip = i.node_ip;
            obj.nodeName = i.node_name;
            obj.Address = i.node_ip;
            obj.role = i.drift_state ? i.drift_state : "pc";
            return obj;
          });
          //this.members = this.members.concat(list);
          console.log(this.members)
        } else {
          this.isHasColony = false;
          // this.activeName = '1'
        }
      }
    },
    // getVrrpInfo() {
    //   getVrrp().then(res => {
    //     if (res.success) {
    //       this.vrIpForm.virtual_ip = res.data.virtual_ip;
    //       this.vrIpForm.enable = res.data.enable;
    //     }
    //   }).catch(e => {
    //     console.log(e)
    //   })
    // },
    async updateCluster() {
      if (this.ruleForm.clustername === "") {
        this.$message({
          type: "error",
          message: "集群名称不能为空"
        })
        return
      }
      let res = await updateClusterName({
        clusterName: this.ruleForm.clustername,
        virtualIp: this.ruleForm.virtualIp
      });
      this.$notify({
        title: res.success ? "成功" : "失败",
        message: res.msg,
        type: res.success ? "success" : "error"
      });
    },
    async joinCluster(json) {
      let res = await joinCluster(json);
      if (res.success) {
        this.members = []
      }
      this.$notify({
        title: res.success ? "成功" : "失败",
        message: res.msg,
        type: res.success ? "success" : "error"
      });
    },
    leave() {
      this.$confirm(`确定退出集群吗?`, {
        center: true,
        cancelButtonClass: "comfirm-class-cancle",
        confirmButtonClass: "comfirm-class-sure"
      }).then(async () => {
        let res = await leave();
        this.$notify({
          title: res.success ? "成功" : "失败",
          message: res.msg,
          type: res.success ? "success" : "error"
        });
        if (res && res.success) {
          this.ruleForm.clustername = "";
          this.ruleForm.clusterpwd = ""
          this.clusterid = "";
          this.isHasColony = false;
          this.activeName = "1";
          this.members = [];
          this.innerNodes = [];
        }
      }).catch(() => {
         this.ruleForm.clustername = "";
          this.ruleForm.clusterpwd = ""
          this.clusterid = "";
          this.isHasColony = false;
          this.activeName = "1";
          this.members = [];
          this.innerNodes = [];
       });
    },
    joinNode(event, node) {
      console.log('join', node)
      let _this = this;
      this.currentCluster.cluster_id = node.cluster_id;
      if (this.activeName === "3") {
        this.manageForm.clustername = node.clusterName;
        this.clusterid = node.cluster_id;
        return;
      }
      if (this.activeName === "2") {
        if (_this.showJoinConfirm) return;
        this.$confirm("是否要加入节点 " + node.nodeName + "?", "加入集群", {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "success"
        })
          .then(() => {
            _this.showJoinConfirm = true;
            let json = {
              //clusterId: this.currentCluster.cluster_id,
              clusterId: node.cluster_id,
              password: this.joinForm.clusterpwd,
              nodeIps: [node.Address]
            };
            this.joinCluster(json).then(() => {
              _this.showJoinConfirm = false;
              this.findCluster();
            }).catch(e => {
              console.log(e);
              _this.showJoinConfirm = false;
            });
          }).catch(e => {
            console.log(e)
          });
        // this.$refs["joinForm"].validate(valid => {
        //   if (valid) {
        //     if (_this.showJoinConfirm) return;
        //     _this.showJoinConfirm = true;
        //     this.$confirm("是否要加入节点 " + node.nodeName + "?", "加入集群", {
        //       confirmButtonText: "确定",
        //       cancelButtonText: "取消",
        //       type: "success"
        //     })
        //       .then(() => {
        //         console.log(this.currentCluster, '选择的集群节点')
        //         this.join("joinForm");
        //         _this.showJoinConfirm = false;
        //       })
        //       .catch(() => {
        //         this.$notify({
        //           type: "info",
        //           duration: 1000,
        //           message: "已取消"
        //         });
        //         _this.showJoinConfirm = false;
        //       });
        //   } else {
        //     console.log("error submit!!");
        //     return false;
        //   }
        // });
      }
    },
    generatePassword() {
      var chars =
        "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
      var uuid = [];
      for (let i = 0; i < 6; i++) {
        uuid[i] = chars[0 | (Math.random() * 50)];
      }
      this.ruleForm.clusterpwd = uuid.join("");
    },
    async getEsClusterNodes() {
      let rsp = await getDevInfo();
      let hostIpAddr = "";
      if (rsp && rsp.success) {
        hostIpAddr = rsp.data.ip;
      }
      if (hostIpAddr.length) {
        rsp = await getEsClusterInfo({ ip: hostIpAddr });
        if (rsp && rsp.success) {
          this.esNodes = rsp.data.map(el => {
            return {
              // buildDate: "2018-06-11T23:38:03.357887Z",
              buildDate: el.buildDate.split("T")[0] + " " + el.buildDate.split("T")[1].slice(0, 8),
              ip: el.ip,
              name: el.name,
              nodeType: el.nodeType,
            }
          })
          if (this.esNodes.length) {
            this.sActiveName = 's-third';
          }
        }
      }
    },
    createEsCluster() {
      createESNode().then(rsp => {
        if (rsp && rsp.success) {
          this.$message({
            type: "success",
            duration: 2000,
            message: "创建成功"
          });
          this.getSearchNodes();
        } else {
          this.$message({
            type: "error",
            duration: 2000,
            message: rsp.msg
          });
        }
      }).catch(rsp => {
        this.$message({
          type: "error",
          duration: 2000,
          message: rsp.msg
        });
      })
    },
    joinESCluster() {
      if (!this.esNodeIp.length) {
        this.$message({
          type: "error",
          duration: 2000,
          message: "请输入正确的ip地址"
        });
        return;
      }
      addESNode({ ip: this.esNodeIp, option: this.esNodeType ? "1" : "2" }).then(rsp => {
        if (rsp && rsp.success) {
          this.$message({
            type: "success",
            duration: 2000,
            message: "加入成功"
          });
          this.getSearchNodes();
        } else {
          this.$message({
            type: "error",
            duration: 2000,
            message: rsp.msg
          });
        }
      }).catch(rsp => {
        this.$message({
          type: "error",
          duration: 2000,
          message: rsp.msg
        });
      })
    },
    onIpBlur(ip) {
      //this.vrIpForm.virtual_ip = ip;
      this.ruleForm.virtualIp = ip;
      console.log(this.ruleForm.virtualIp)
    }
  },
  created() { }
};
</script>
<style lang="scss">
.s-cluster-management {
  width: 100%;
  height: 100%;
  overflow: auto;
  .el-form-item {
    width: 500px;
  }
  .el-form-item.is-required:not(.is-no-asterisk) > .el-form-item__label:before,
  .el-form-item.is-required:not(.is-no-asterisk)
    .el-form-item__label-wrap
    > .el-form-item__label:before {
    display: none;
  }
  .ip-input-comp {
    width: 400px;
    max-width: 400px;
  }
  .el-button {
    float: right;
  }
  .el-form-item__content {
    text-align: left;
    input {
      max-width: 400px !important;
    }
  }
  #e-alaycluster,
  #e-dbcluster {
    .el-tabs__header {
      border: 0px solid #dcdfe6;
      .el-tabs__item {
        padding: 5px 50px;
        height: 50px;
        font-family: PingFangSC-Regular;
        font-size: 14px;
        color: #222222;
        text-align: center;
        border: 0px solid transparent;
      }
      .el-tabs__item:nth-child(2) {
        padding-left: 50px;
      }
      .el-tabs__item:last-child {
        padding-right: 50px;
      }
      .el-tabs__item.is-active {
        color: #ff7733;
        font-weight: bold;
        // border-right-color: #fff;
        // border-left-color: #fff;
      }
      .el-tabs__item:not(.is-disabled):hover {
        color: #ff7733;
      }
    }
    .el-tabs__active-bar {
      background-color: #ff7733;
    }
    .el-form-item__content {
      text-align: left;
      input {
        max-width: 420px;
      }
    }
  }
  #h-alaycluster {
    .el-form-item__content {
      text-align: left;
      // input {
      //   max-width: 360px;
      // }
    }
  }
}
.ui-top-view {
  height: 30px;
  line-height: 30px;
}
</style>
src/pages/systemSettings/components/LogManagement.vue
New file
@@ -0,0 +1,190 @@
<template>
  <div class="s-log-management">
    <div class="top">
      <b>日志类型:</b>
      <el-select v-model="logValue" placeholder="请选择" size="small">
        <el-option
          v-for="item in logOptions"
          :key="item.value"
          :label="item.label"
          :value="item.value"
        ></el-option>
      </el-select>
      <b>时间:</b>
      <el-date-picker
        v-model="timeValue"
        type="datetimerange"
        size="small"
        range-separator="至"
        start-placeholder="开始日期"
        end-placeholder="结束日期"
      ></el-date-picker>
      <el-input
        v-model="searchValue"
        placeholder="请输入内容"
        clearable
        style="width: 150px;margin: 0px 10px;"
        size="small"
      ></el-input>
      <el-button type="primary" size="small">搜索</el-button>
      <el-button type="danger" size="small" @click="delSelected">批量删除</el-button>
      <el-button type="text" size="small" style="font-size: 13px;font-weight: 600;">导出</el-button>
    </div>
    <div class="foot-table s-table">
      <el-table
        ref="multipleTable"
        highlight-current-row
        :data="tableData"
        style="width: 100%"
        :header-cell-style="{background:'#f8f8f8',color:'#222222'}"
        @selection-change="handleSelectionChange"
      >
        <el-table-column type="selection" width="55"></el-table-column>
        <el-table-column :align="'center'" sortable prop="index" label="序号"></el-table-column>
        <el-table-column :align="'center'" sortable prop="logtype" label="日志类型"></el-table-column>
        <el-table-column :align="'center'" sortable prop="username" label="用户名"></el-table-column>
        <el-table-column :align="'center'" sortable prop="ipaddress" label="IP地址"></el-table-column>
        <el-table-column :align="'center'" sortable prop="operation" label="操作功能"></el-table-column>
        <el-table-column :align="'center'" sortable prop="operatetime" label="操作时间"></el-table-column>
        <el-table-column :align="'center'" sortable prop="operateinfo" label="操作信息"></el-table-column>
        <el-table-column label="操作" :align="'center'">
          <template slot-scope="scope">
            <el-button
              type="text"
              style="color: red;font-size:16px"
              @click="handleDelete(scope.$index, scope.row)"
              icon="el-icon-delete"
            ></el-button>
          </template>
        </el-table-column>
      </el-table>
    </div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      tableData: [
        {
          index: "1",
          logtype: "异常",
          username: "admin",
          ipaddress: "192.168.10.110",
          operation: "添加摄像机",
          operatetime: "2019-5-31 16:38:21",
          operateinfo: "添加操作"
        },
        {
          index: "2",
          logtype: "信息",
          username: "admin",
          ipaddress: "192.168.10.108",
          operation: "删除摄像机",
          operatetime: "2019-5-31 16:38:21",
          operateinfo: "添加操作"
        },
        {
          index: "3",
          logtype: "操作",
          username: "admin",
          ipaddress: "192.168.10.110",
          operation: "添加摄像机",
          operatetime: "2019-5-31 16:38:21",
          operateinfo: "添加操作"
        },
        {
          index: "4",
          logtype: "异常",
          username: "admin",
          ipaddress: "192.168.10.110",
          operation: "添加摄像机",
          operatetime: "2019-5-31 16:38:21",
          operateinfo: "添加操作"
        },
        {
          index: "5",
          logtype: "异常",
          username: "admin",
          ipaddress: "192.168.10.110",
          operation: "删除摄像机",
          operatetime: "2019-5-31 16:38:21",
          operateinfo: "添加操作"
        },
        {
          index: "6",
          logtype: "异常",
          username: "admin",
          ipaddress: "192.168.10.110",
          operation: "添加摄像机",
          operatetime: "2019-5-31 16:38:21",
          operateinfo: "添加操作"
        }
      ],
      multipleSelection: [],
      logOptions: [
        {
          value: "全部类型",
          label: "全部类型"
        },
        {
          value: "其他类型",
          label: "其他类型"
        }
      ],
      logValue: "全部类型",
      timeValue: [
        new Date(2000, 10, 10, 10, 10),
        new Date(2000, 10, 11, 10, 10)
      ],
      searchValue: ""
    };
  },
  methods: {
    handleDelete(index, row) {
      console.log(index, row);
    },
    handleSelectionChange(val) {
      this.multipleSelection = val;
    },
    delSelected() {
      console.log(this.multipleSelection);
    }
  }
};
</script>
<style lang="scss">
.s-log-management {
  height: 100%;
  width: 100%;
  .top {
    width: 100%;
    margin-top: 10px;
    margin-bottom: 20px;
    overflow-y: auto;
    min-width: 1156px;
    height: 40px;
    text-align: left;
    b {
      padding: 0px 10px;
    }
  }
  .export {
    display: inline-block;
    padding-right: 10px;
    box-sizing: border-box;
    margin-top: 20px;
    b:hover {
      color: #2249b4;
    }
  }
  .clear-searching {
    cursor: pointer;
    text-decoration: underline;
    width: 40px;
    font-size: 13px;
    color: #3d68e1;
  }
}
</style>
src/pages/systemSettings/components/RadioSet.vue
New file
@@ -0,0 +1,189 @@
<template>
  <div class="s-radio-set">
    <div class="add-btn">
      <el-button size="mini" type="primary" @click="handleAdd()">添加</el-button>
    </div>
    <el-table
      border
      highlight-current-row
      :data="tableData"
      style="width: 100%; margin-top:40px; color:#000"
      :header-cell-style="{background:'#f8f8f8',color:'#222222'}"
    >
      <el-table-column align="center" type="index" label="序号" width="100px"></el-table-column>
      <el-table-column :align="'center'" label="广播名称">
        <template slot-scope="{row}">
          <el-input v-if="row.edit" :autofocus="row.edit" v-model="row.radiosName" size="small" />
          <span v-else>{{ row.radiosName }}</span>
        </template>
      </el-table-column>
      <el-table-column :align="'center'" label="IP地址">
        <template slot-scope="{row}">
          <el-input v-if="row.edit" v-model="row.ipAddress" size="small" />
          <span v-else>{{ row.ipAddress }}</span>
        </template>
      </el-table-column>
      <el-table-column :align="'center'" label="连接测试">
        <template slot-scope="{row}">
          <i v-show="row.isCon" class="el-icon-success" style="color:green; font-size:18px"></i>
          <el-button type="text" @click="handleTest(row)">连接测试</el-button>
        </template>
      </el-table-column>
      <el-table-column label="操作" :align="'center'">
        <template slot-scope="scope">
          <template v-if="scope.row.edit">
            <el-button size="mini" type="info" @click="handleCancel(scope.row)">取消</el-button>
            <el-button size="mini" type="primary" @click="handleSave(scope.row)">保存</el-button>
          </template>
          <template v-else>
            <el-button
              type="text"
              style="color: black;font-size:18px"
              @click="handleEdit(scope.row)"
              icon="el-icon-edit"
            ></el-button>
            <el-button
              type="text"
              style="color: red;font-size:18px"
              @click="handleDelete(scope.$index)"
              icon="el-icon-delete"
            ></el-button>
          </template>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>
<script>
export default {
  filters: {
    isCon(r) {
      return r.isCon ? r.isCon : false
    }
  },
  data() {
    return {
      radioName: "",
      ipAddress: "",
      tableData: [
        {
          index: "1",
          radiosName: "操场",
          ipAddress: "192.168.1.101",
          edit: false,
          isCon: false
        },
        {
          edit: false,
          index: "2",
          radiosName: "教室",
          ipAddress: "192.168.12.61",
          isCon: false
        },
        {
          edit: false,
          index: "3",
          radiosName: "保安室",
          ipAddress: "192.168.13.121",
          isCon: false
        }
      ]
    };
  },
  mounted() {
    this.testAll()
  },
  methods: {
    testAll() {
      this.tableData.forEach(l => {
        this.$set(l, "isCon", false)
      })
    },
    handleEdit(row) {
      console.log(row);
      row.edit = true;
    },
    handleCancel(row) {
      row.edit = false;
      console.log(row);
    },
    handleDelete(index) {
      this.$confirm("确认删除该广播吗?", {
        center: true,
        cancelButtonClass: "comfirm-class-cancle",
        confirmButtonClass: "comfirm-class-sure"
      })
        .then(() => {
          this.tableData.splice(index, 1)
          this.$notify({
            type: "success",
            message: "删除成功!"
          });
        })
        .catch(() => { });
    },
    handleSave(row) {
      console.log(row);
      row.edit = false;
      this.$notify({
        message: "保存成功",
        type: "success"
      });
    },
    handleTest(row) {
      this.$set(row, 'isCon', true)
    },
    handleAdd() {
      this.tableData.push({
        radiosName: "",
        ipAddress: "",
        edit: true,
        isCon: false
      })
    }
  }
};
</script>
<style lang="scss">
.s-radio-set {
  width: 100%;
  height: 100%;
  .el-dialog {
    border-radius: 8px;
    border: 1px solid #ccc;
    .el-dialog__header {
      border-bottom: 1px solid #ccc;
    }
  }
  .add-btn {
    float: right;
    margin-bottom: 7px;
  }
}
.e-message {
  width: 331px;
}
.e-confirm {
  border-color: #ff0000 !important;
  background-color: #ff0000 !important;
}
.e-confirm:hover {
  border-color: #f83131d6 !important;
  background-color: #f83131d6 !important;
}
.e-cancel {
  border-color: #eaeaea !important;
  background-color: #eaeaea !important;
}
.e-cancel:hover {
  border-color: #e9e9e9 !important;
  background-color: #e9e9e9 !important;
}
</style>
src/pages/systemSettings/components/SystemMaintenance.vue
New file
@@ -0,0 +1,514 @@
<template>
    <el-tabs
      id="systemMaintenance"
      v-model="activeName"
      v-loading="loading"
      :element-loading-text="loadingText"
    >
      <el-tab-pane label="设备维护" name="first" v-if="isShow('videoSystem:sysManage:sysfix')">
        <div class="s-system-maintenance">
        <div class="box-card">
          <div class="ui-top-view">
            <div class="ui-top-title">重启</div>
          </div>
          <el-divider></el-divider>
          <div class="box-card-content">
            <el-row>
              <el-col :span="1">
                <el-button type="primary" size="small" style="width:80px" @click="reboot">重启</el-button>
              </el-col>
              <el-col :span="23">
                <b class="card-text">重启节点</b>
              </el-col>
            </el-row>
            <el-row style="margin-top:20px">
              <el-col>
                <vue-cron :expression="rebootCron" @update="setRebootCron" />
              </el-col>
            </el-row>
          </div>
        </div>
        <!--
        <div class="box-card">
          <div class="ui-top-view">
            <div class="ui-top-title">恢复默认值</div>
          </div>
          <el-divider></el-divider>
          <div class="box-card-content">
            <el-row>
              <el-col :span="1">
                <el-button type="primary" size="small">简单恢复</el-button>
              </el-col>
              <el-col :span="23">
                <b class="card-text">简单恢复设备参数</b>
              </el-col>
            </el-row>
            <el-row style="margin-top:20px">
              <el-col :span="1">
                <el-button type="primary" size="small">完全恢复</el-button>
              </el-col>
              <el-col :span="23">
                <b class="card-text">完全恢复设备参数到出厂设置</b>
              </el-col>
            </el-row>
          </div>
        </div>
        <div class="box-card">
          <div class="ui-top-view">
            <div class="ui-top-title">参数导入导出</div>
          </div>
          <el-divider></el-divider>
          <div class="box-card-content">
            <el-row :gutter="4">
              <el-col :span="1">
                <el-button type="info" size="small" style="width:80px">导入</el-button>
              </el-col>
              <el-col :span="3" style="padding-left:30px">
                <el-input placeholder="上传参数文件" size="small" :readonly="true">
                  <el-upload slot="suffix" action="https://jsonplaceholder.typicode.com/posts/">
                    <el-button
                      type="text"
                      icon="el-icon-upload2"
                      size="small"
                      style="font-size:18px; color:#0088ff"
                    ></el-button>
                  </el-upload>
                </el-input>
              </el-col>
            </el-row>
            <el-row style="margin-top:20px">
              <el-col :span="1">
                <el-button type="primary" size="small">设备参数</el-button>
              </el-col>
              <el-col :span="23">
                <b class="card-text">参数导出</b>
              </el-col>
            </el-row>
          </div>
        </div>
        -->
        <div class="box-card">
          <div class="ui-top-view">
            <div class="ui-top-title">升级</div>
          </div>
        </div>
        <el-divider></el-divider>
        <div class="box-card-content">
          <el-row :gutter="4">
            <el-col :span="6">
              <file-uploader
                single
                uploadPlaceholder="上传升级文件"
                url="/data/api-v/sysset/patchUpdate"
                @complete="onFileUpload"
                @file-added="onFileAdded"
              />
            </el-col>
            <el-col :span="2">
              <el-button
                type="primary"
                size="small"
                style="width:80px"
                @click="upgrade"
                :disabled="!fileAdded"
                :loading="upgrading"
              >升级</el-button>
            </el-col>
            <el-col :span="16" class="upload-msg">
              <span v-html="patchUpdateStatus"></span>
            </el-col>
          </el-row>
        </div>
      </div>
      </el-tab-pane>
      <el-tab-pane label="数据库维护" name="second" v-if="isShow('videoSystem:sysManage:dbfix')">
        <div class="box">
          <p class="title">
            <label>数据清理</label>
          </p>
          <div class="range">
            <div class="left">
              <p>选择数据范围:</p>
            </div>
            <div class="middle">
              <el-date-picker
                v-model="dataRange"
                type="daterange"
                range-separator="至"
                start-placeholder="开始日期"
                end-placeholder="结束日期"
                style="height:38px"
                :picker-options="pickerOptions"
              ></el-date-picker>
            </div>
            <div class="right">
              <el-button @click="deleteData" style="height:38px;background:#ff0000;color:white">删除数据</el-button>
            </div>
          </div>
          <div class="tip">
            <i class="iconfont icontishi-zhuyi"></i>
            <p class="zhuyi">请注意,按以上日期范围删除的数据不可恢复,立即生效,请谨慎操作</p>
          </div>
        </div>
      </el-tab-pane>
    </el-tabs>
</template>
<script>
import { rebootServer, getDevInfo, getRebootTask, setRebootTask, fileUpload, doUpgrade,deleteDate } from "@/api/system"
import VueCron from "@/components/subComponents/VueCron"
import FileUploader from "@/components/subComponents/FileUpload/index"
export default {
  components: {
    VueCron,
    FileUploader
  },
  data() {
    return {
      timer: null,
      buttonAuthority: sessionStorage.getItem("buttonAuthoritys") || [],
      rebootCron: "",
      activeName: "first",
      restartValue: "不重启",
      restartTimeValue: new Date(2019, 9, 10, 18, 40),
      loading: false,
      loadingText: '',
      probeSum: 0,
      patchUpdateStatus: "",
      dataRange: [
        this.$moment().format("YYYY-MM-DD HH:mm:ss"),
        this.$moment().format("YYYY-MM-DD HH:mm:ss")
      ],
      fileUploadUrl: fileUpload,
      patchFile: {},
      pickerOptions: {
        disabledDate(time) {
          var day = new Date()
          day.setTime(day.getTime() - 24 * 60 * 60 * 1000)
          return time.getTime() > day;
        },
      },
      upgrading: false,
      fileAdded: false
    };
  },
  mounted() {
    this.getRebootCron()
    if (!this.isShow('videoSystem:sysManage:sysfix')) {
      console.log("默认显示数据库维护")
      this.activeName = "second"
    }
  },
  computed: {
    isAdmin() {
      if (
        sessionStorage.getItem('userInfo') &&
        sessionStorage.getItem('userInfo') !== ''
      ) {
        let loginName = JSON.parse(sessionStorage.getItem('userInfo')).username
        return (
          loginName === 'superadmin' || loginName === 'basic'
        )
      }
      return false;
    }
  },
  methods: {
    isShow (authority) {
      if (this.isAdmin) {
        return true
      } else if (
        this.buttonAuthority.indexOf(',' + authority + ',') > -1
      ) {
        return true
      } else {
        return false
      }
    },
    format(array) {
      return [
        this.$moment(array[0]).format("YYYY-MM-DD"),
        this.$moment(array[1]).format("YYYY-MM-DD")
      ];
    },
    getRebootCron() {
      getRebootTask().then(rsp => {
        this.rebootCron = rsp.data
      })
    },
    setRebootCron(value) {
      this.rebootCron = value
      setRebootTask({ task: value }).then(rsp => {
        if (rsp && rsp.success) {
          this.$notify({
            type: "success",
            message: "配置成功"
          })
        }
      }).catch(err => {
        this.$notify({
          type: "error",
          message: "配置失败"
        })
      })
    },
    reboot() {
      this.$confirm('确定要重启该节点吗?', {
        center: true,
        cancelButtonClass: 'comfirm-class-cancle',
        confirmButtonClass: 'comfirm-class-sure'
      }).then(() => {
        this.loading = true;
        this.loadingText = "智能计算节点正在重启,请耐心等待..."
        rebootServer().then(rsp => {
          this.probeServer(this.reLogin)
        }).catch(err => {
          if (err.status == 400) {
            this.loading = false;
            this.$notify({
              type: "error",
              message: "重启计算节点失败"
            })
          } else {
            this.probeServer(this.reLogin)
          }
        })
      })
    },
    deleteData() {
      var timeRange = this.format(this.dataRange);
      var showStartTime = timeRange[0]
      var showEndTime = timeRange[1]
      console.log("时间:",showStartTime,showEndTime)
      this.$confirm("提示:"+showStartTime+" 至 "+showEndTime+" 产生的全部数据将被删除,此操作立即生效,不可恢复,是否删除?", {
        center: true,
        cancelButtonClass: "comfirm-class-cancle",
        confirmButtonClass: "comfirm-class-sure"
      }).then(() => {
        this.loading = true
        this.loadingText = "正在删除数据,请稍候!"
        var param = {
          startTime: showStartTime,
          endTime: showEndTime
        }
        deleteDate(param).then(resp => {
          if (resp.success) {
            this.$message({
              type: "success",
              message: "删除数据成功"
            })
            this.loading = false
          }
        }).catch(err => {
          this.$message({
            type: "error",
            message: "删除数据失败!"
          })
          this.loading = false
        })
      }).catch(() => {
        console.log("取消了!")
      })
    },
    reLogin() {
      this.$router.push("/")
    },
    probeServer(callback) {
      this.probeSum++;
      let _this = this
      if (this.probeSum > 60) {
        this.$confirm('连接服务器失败, 请刷新页面或联系管理员', '失败', {
          type: 'error',
          cancelButtonClass: 'comfirm-class-cancle',
          confirmButtonClass: 'comfirm-class-sure'
        }).then(() => {
          // _this.$router.push("/")
          callback()
        })
        return
      }
      this.timer = setTimeout(() => {
        getDevInfo().then(() => {
          // _this.$router.push("/")
          callback()
        }).catch(err => {
          _this.probeServer(callback)
        })
      }, 10000)
    },
    onFileUpload(file) {
      this.patchUpdateStatus = `<span style="color:green">上传成功, 点击升级按钮开始升级</span>`
      this.patchFile = { ...file }
      this.fileAdded = true
    },
    onFileAdded() {
      this.patchUpdateStatus = ""
    },
    upgrade() {
      this.upgrading = true
      this.patchUpdateStatus = `<span style="color:red">正在升级...</span>`
      doUpgrade(this.patchFile).then(rsp => {
        this.upgrading = false
        if (rsp && rsp.success) {
          clearTimeout(this.timer)
          this.doneUpgrade()
        }
      }).catch(err => {
        if (err.code) {
          this.upgrading = false
          this.patchUpdateStatus = `<span style="color:red">${err.data}</span>`
          clearTimeout(this.timer)
        } else {
          this.probeServer(this.doneUpgrade)
        }
      })
    },
    doneUpgrade() {
      this.upgrading = false
      this.patchUpdateStatus = `<span style="color:green">升级成功</span>`
      let _this = this
      this.$confirm('升级成功, 请重新登录系统', '成功', {
        type: 'success',
        cancelButtonClass: 'comfirm-class-cancle',
        confirmButtonClass: 'comfirm-class-sure'
      }).then(() => {
        _this.reLogin()
      })
    }
  }
};
</script>
<style lang="scss">
.s-system-maintenance {
  width: 100%;
  height: 100%;
  .box-card {
    text-align: left;
    height: auto;
    margin: 10px 0px;
    .box-card-content {
      padding-bottom: 40px;
      .card-text {
        padding: 0 30px;
        line-height: 32px;
      }
    }
  }
  .upload-icon {
    font-size: 18px;
    color: #0088ff;
  }
  .upload-msg {
    padding-left: 10px;
    text-align: left;
    span {
      line-height: 32px;
      font-size: 13px;
    }
  }
}
.box{
  width: 50%;
  min-width: 700px;
  height: 270px;
  border: 1px solid #eee;
  .title {
    font-size:20px;
    font-weight: bold;
    text-align: left;
    padding: 20px;
    border-bottom: 1px solid #eee;
  }
  .range {
    width: 100%;
    padding-top: 30px;
    height: 38px;
    .left {
      width: 120px;
      float: left;
      text-align: right;
      font-size: 14px;
      p {
        height: 38px;
        line-height: 38px;
      }
    }
    .middle {
      width: 50%;
      min-width: 400px;
      height: 38px;
      float: left;
    }
    .right {
      width: 20%;
      height: 38px;
      float: left;
    }
  }
  .tip {
    width: 100%;
    padding: 30px 0px 0px 30px;
    height: 34px;
    .zhuyi {
      font-size: 14px;
      height: 34px;
      line-height: 34px;
      margin-left: 20px;
      float: left;
    }
    i {
      font-size: 32px;
      color: #e99038;
      float: left;
    }
  }
}
#systemMaintenance{
  .el-tabs__header {
      border: 0px solid #dcdfe6;
      .el-tabs__item {
        padding: 5px 50px;
        height: 50px;
        font-family: PingFangSC-Regular;
        font-size: 14px;
        color: #222222;
        text-align: center;
        border: 0px solid transparent;
      }
      .el-tabs__item:nth-child(2) {
        padding-left: 50px;
      }
      .el-tabs__item:last-child {
        padding-right: 50px;
      }
      .el-tabs__item.is-active {
        color: #ff7733;
        font-weight: bold;
        // border-right-color: #fff;
        // border-left-color: #fff;
      }
      .el-tabs__item:not(.is-disabled):hover {
        color: #ff7733;
      }
  }
  .el-tabs__active-bar {
    background-color: #ff7733;
  }
  .el-tabs__content {
    padding-left: 15px !important;
  }
}
</style>
src/pages/systemSettings/index/App.vue
New file
@@ -0,0 +1,161 @@
<template>
  <div class="s-system-manage">
    <basic-setting v-show="activeName === 'basic'"></basic-setting>
  </div>
</template>
<script>
import BasicSetting from "../components/BasicSetting";
export default {
  name: 'settings',
  components: {
    BasicSetting
  },
  data() {
    return {
      activeName: "basic",
      buttonAuthority: sessionStorage.getItem("buttonAuthoritys") || [],
      loginName: JSON.parse(sessionStorage.getItem("userInfo")).username || "用户名"
    }
  },
  computed: {
    isAdmin() {
      if (
        sessionStorage.getItem("userInfo") &&
        sessionStorage.getItem("userInfo") !== ""
      ) {
        let loginName = JSON.parse(sessionStorage.getItem("userInfo")).username;
        return loginName === "superadmin" || loginName === "basic";
      }
      return false;
    }
  },
  methods: {
    isShow(authority) {
      if (this.isAdmin) {
        return true;
      } else if (this.buttonAuthority.indexOf("," + authority + ",") > -1) {
        return true;
      } else {
        return false;
      }
    },
  },
  created() {
    if (this.isShow('videoSystem:base')) {
      this.activeName = "basic"
    } else if (this.isShow('videoSystem:permission')) {
      this.activeName = "user"
    } else if (this.isShow('videoSystem:broadcast')) {
      this.activeName = "radio"
    } else if (this.isShow('videoSystem:eventPush')) {
      this.activeName = "event"
    } else if (this.isShow('videoSystem:logManage')) {
      this.activeName = "log"
    } else if (this.isShow('videoSystem:sysManage')) {
      this.activeName = "system"
    }
  },
};
</script>
<style lang="scss">
.s-system-manage {
  width: 100% !important;
  min-width: 1067px;
  height: 100%;
  box-sizing: border-box;
  padding: 10px;
  background-color: #f8f9fb;
  .s-system-manage-breadcrumb {
    height: 5%;
    box-sizing: border-box;
    border: 1px solid #e4e7ed;
    box-shadow: #e4e7ed 0px 0px 9px inset;
    box-shadow: #e4e7ed 0px 0px 9px inset;
    border-radius: 5px;
  }
  .el-tabs--border-card {
    border: 0px solid #dcdfe6;
    -webkit-box-shadow: none;
    box-shadow: none;
    .el-tabs__header {
      border: 0px solid #dcdfe6;
      .el-tabs__item {
        padding: 5px 50px;
        height: 50px;
        font-family: PingFangSC-Regular;
        font-size: 15px;
        color: #222222;
        text-align: center;
        border: 0px solid transparent;
      }
      .el-tabs__item:nth-child(2) {
        padding-left: 50px !important;
      }
      .el-tabs__item:last-child {
        padding-right: 50px !important;
      }
      .el-tabs__item.is-active {
        color: #3d68e1;
        // border-right-color: #fff;
        // border-left-color: #fff;
      }
      .el-tabs__item:not(.is-disabled):hover {
        color: #3d68e1;
      }
    }
  }
  .el-tabs__header {
    margin-bottom: 0;
  }
  .el-tabs__content {
    height: calc(100% - 64px);
    box-sizing: border-box;
    overflow-y: auto;
    padding: 20px 40px !important;
    background: #fff;
    .el-tab-pane {
      width: 100%;
      .s-title {
        text-align: left;
        padding: 15px 0px;
        font-size: 16px;
      }
    }
  }
  .s-table {
    border: 1px solid #e8e8e9;
    margin-top: 40px;
  }
  .ui-top-title {
    padding-bottom: 10px;
    /* border-bottom: 1px solid #ebebeb; */
    position: relative;
    text-align: left;
    padding-left: 15px;
    font-size: 16px;
    font-weight: bold;
  }
  .ui-top-title:before {
    content: " ";
    border-left: 4px solid #f53d3d;
    display: inline-block;
    height: 16px;
    position: absolute;
    top: 50%;
    left: 0;
    margin-top: -13px;
  }
  .el-button--text {
    color: #3d68e1;
    text-decoration: underline;
  }
}
</style>
src/pages/systemSettings/index/main.ts
New file
@@ -0,0 +1,12 @@
import Vue from 'vue';
import App from './App.vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import "@/assets/css/element-variables.scss";
Vue.use(ElementUI)
new Vue({
  el: '#app',
  render: h => h(App)
})