鸿蒙智能电子锁前端项目
whychdw
2024-10-11 d5ca37c6301ac1178702e0334c4feee6abf3ba9e
内容提交
10个文件已修改
8个文件已添加
834 ■■■■■ 已修改文件
.env.build_prod 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
.env.dev_prod 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
components.d.ts 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
mock/demo/user.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/user.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/user.png 补丁 | 查看 | 原始文档 | blame | 历史
src/components/HdwCard/index.vue 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/HdwTree/index.vue 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/key-hdw.svg 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/people-hdw.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/safe-lock-hdw.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/modules/system.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/resize.js 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dashboard/components/LineChart.vue 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dashboard/components/MenuCard.vue 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dashboard/index.vue 295 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/device/lock/index.vue 152 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vite.config.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.env.build_prod
@@ -2,4 +2,4 @@
NODE_ENV = 'production'
# 打包模式连生产服
VUE_APP_BASE_API = '/api-prod'
VUE_APP_BASE_API = '/'
.env.dev_prod
@@ -2,5 +2,5 @@
NODE_ENV = 'development'
# 开发模式连生产服
VUE_APP_BASE_API = '/api-prod'
VUE_APP_COOKIE = ''
VUE_APP_BASE_API = '/'
VUE_APP_COOKIE = ''
components.d.ts
@@ -13,12 +13,13 @@
    DropdownMenu: typeof import('./src/components/Share/DropdownMenu.vue')['default']
    Dropzone: typeof import('./src/components/Dropzone/index.vue')['default']
    EditorImage: typeof import('./src/components/Tinymce/components/EditorImage.vue')['default']
    ElAvatar: typeof import('element-plus/es')['ElAvatar']
    ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
    ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
    ElButton: typeof import('element-plus/es')['ElButton']
    ElCard: typeof import('element-plus/es')['ElCard']
    ElCol: typeof import('element-plus/es')['ElCol']
    ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
    ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
    ElDialog: typeof import('element-plus/es')['ElDialog']
    ElDropdown: typeof import('element-plus/es')['ElDropdown']
    ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
@@ -29,18 +30,22 @@
    ElInput: typeof import('element-plus/es')['ElInput']
    ElMenu: typeof import('element-plus/es')['ElMenu']
    ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
    ElProgress: typeof import('element-plus/es')['ElProgress']
    ElPagination: typeof import('element-plus/es')['ElPagination']
    ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
    ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
    ElRow: typeof import('element-plus/es')['ElRow']
    ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
    ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
    ElSwitch: typeof import('element-plus/es')['ElSwitch']
    ElTable: typeof import('element-plus/es')['ElTable']
    ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
    ElTag: typeof import('element-plus/es')['ElTag']
    ElTooltip: typeof import('element-plus/es')['ElTooltip']
    ElTree: typeof import('element-plus/es')['ElTree']
    ErrorLog: typeof import('./src/components/ErrorLog/index.vue')['default']
    GithubCorner: typeof import('./src/components/GithubCorner/index.vue')['default']
    Hamburger: typeof import('./src/components/Hamburger/index.vue')['default']
    HdwCard: typeof import('./src/components/HdwCard/index.vue')['default']
    HdwTree: typeof import('./src/components/HdwTree/index.vue')['default']
    HeaderSearch: typeof import('./src/components/HeaderSearch/index.vue')['default']
    ImageCropper: typeof import('./src/components/ImageCropper/index.vue')['default']
    Keyboard: typeof import('./src/components/Charts/Keyboard.vue')['default']
mock/demo/user.js
@@ -26,7 +26,7 @@
export default [
  // user login
  {
    url: '/vue-element-admin/user/login',
    url: '/user/login',
    method: 'post',
    response: config => {
      const { username } = config.body;
src/api/user.js
@@ -2,7 +2,7 @@
export function login(data) {
  return request({
    url: '/vue-element-admin/user/login',
    url: '/user/login',
    method: 'post',
    data
  });
src/assets/images/user.png
src/components/HdwCard/index.vue
New file
@@ -0,0 +1,57 @@
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
  name: 'HdwCard',
  props: {
    isFull: {
      type: Boolean,
      default: false
    },
    title: {
      type: String,
      default: ''
    }
  }
});
</script>
<template>
  <div class="hdw-card" :class="[isFull && 'is-full', title && 'is-has-title']">
    <div v-if="title" class="hdw-card-title">
      {{title}}
    </div>
    <div class="hdw-card-content">
      <slot></slot>
    </div>
  </div>
</template>
<style scoped lang="scss">
.hdw-card {
  box-shadow: 0 0 12px rgba(0,0,0,0.2);
  padding: 8px;
  border-radius: 4px;
  &.is-full {
    height: 100%;
    .hdw-card-content {
      padding-top: 8px;
      height: 100%;
    }
    &.is-has-title {
      .hdw-card-content {
        height: calc(100% - 30px);
      }
    }
  }
  .hdw-card-title {
    margin-left: -8px;
    margin-right: -8px;
    padding: 4px 16px;
    border-bottom: 1px solid rgba(128, 128, 128, 0.2);
    font-weight: 700;
    font-size: 14px;
  }
}
</style>
src/components/HdwTree/index.vue
New file
@@ -0,0 +1,69 @@
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
  name: 'HdwTree',
  data() {
    return {
      defaultProps: {
        children: 'children',
        label: 'label'
      },
      data: [
        {
          label: '河南省',
          children: [
            {
              label: '驻马店市',
              children: [
                {
                  label: '上蔡县'
                },
                {
                  label: '确山县'
                }
              ]
            }
          ]
        },
        {
          label: '湖北省',
          children: [
            {
              label: '武汉市',
              children: [
                {
                  label: '东西湖区'
                },
                {
                  label: '青山区'
                }
              ]
            }
          ]
        }
      ]
    };
  },
  methods: {
    handleNodeClick(data) {
      console.log(data);
    }
  }
});
</script>
<template>
  <el-scrollbar>
    <el-tree
        style="max-width: 600px"
        :data="data"
        :props="defaultProps"
        @node-click="handleNodeClick"
    />
  </el-scrollbar>
</template>
<style scoped lang="scss">
</style>
src/icons/svg/key-hdw.svg
@@ -1 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1728439454077" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="865" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M632.527307 498.500942q39.532957 14.463277 72.316384 39.532957t56.888889 58.335217 37.122411 73.280603 13.016949 83.404896q0 56.888889-21.212806 106.06403t-58.335217 85.815443-86.297552 57.853107-105.099812 21.212806-105.099812-21.212806-86.297552-57.853107-58.335217-85.815443-21.212806-106.06403q0-44.354049 13.499058-84.369115t37.60452-73.280603 57.370998-58.335217 72.798493-39.532957l0-404.971751q0-17.355932 6.26742-33.747646t17.838041-29.408663 27.480226-21.212806 34.229755-8.195857 35.676083 8.195857 30.854991 21.212806 21.212806 29.408663 7.713748 33.747646l0 405.93597zM542.854991 846.583804q38.568738 0 65.566855-26.998117t26.998117-66.531073q0-38.568738-26.998117-65.566855t-65.566855-26.998117q-39.532957 0-66.531073 26.998117t-26.998117 65.566855q0 39.532957 26.998117 66.531073t66.531073 26.998117zM751.126177 121.491525q61.709981 0 61.709981 57.853107l0 62.6742q0 33.747646-16.873823 46.764595t-37.122411 13.016949l-66.531073 0 0-180.308851 58.817326 0z" p-id="866"></path></svg>
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1728524053330" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1585" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M446.680407 513.704726h136.078739v510.295274h-136.078739z" p-id="1586"></path><path d="M532.410013 666.793308h115.666928v136.07874H532.410013zM532.410013 843.69567h149.686613v136.07874H532.410013zM511.998202 585.146065A290.528109 290.528109 0 0 1 219.428912 292.576775 290.528109 290.528109 0 0 1 511.998202 0.007484a290.528109 290.528109 0 0 1 292.56929 292.569291 290.528109 290.528109 0 0 1-292.56929 292.56929z m0-449.059841A153.768976 153.768976 0 0 0 355.507651 292.576775a153.768976 153.768976 0 0 0 156.490551 156.49055 153.768976 153.768976 0 0 0 156.49055-156.49055A153.768976 153.768976 0 0 0 511.998202 136.086224z" p-id="1587"></path></svg>
src/icons/svg/people-hdw.svg
New file
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1728459304110" class="icon" viewBox="0 0 1028 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1466" xmlns:xlink="http://www.w3.org/1999/xlink" width="128.5" height="128"><path d="M815.814506 299.350645c0 165.306834-134.011812 299.350645-299.350645 299.350645s-299.350645-134.011812-299.350645-299.350645c0-165.306834 134.011812-299.350645 299.350645-299.350645s299.350645 134.011812 299.350645 299.350645z" p-id="1467"></path><path d="M763.52814 612.780851c-69.75782 55.070279-156.219118 85.661323-247.064279 85.661323-91.901128 0-179.1944-31.295022-249.27221-87.421268-184.698228 67.805881-267.19165 304.758476-267.19165 412.979094l1027.711884 0c0-107.260648-83.133402-342.549295-264.183744-411.18715z" p-id="1468"></path></svg>
src/icons/svg/safe-lock-hdw.svg
New file
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1728521944946" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1525" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M514.062027 1018.052384h-12.821041c-57.694685-18.936986-230.764712-113.621918-307.690959-195.682192-76.912219-88.372603-115.375342-170.432877-115.375342-265.117808v-378.739726c0-12.624658 6.410521-25.249315 19.217534-31.561644l403.848767-138.871233h19.217535l397.424219 138.871233c12.821041 6.312329 19.231562 18.936986 19.231561 31.561644v366.115068c0 100.99726-32.038575 183.057534-115.375342 265.117808-70.515726 94.684932-243.585753 189.369863-294.855891 208.30685h-12.821041z m161.52548-586.906302h-35.629589v-80.938082c0-67.766356-56.348055-122.82389-125.713534-122.82389-69.295342 0-125.727562 55.127671-125.727562 122.837917v80.938083h-35.629589c-17.099397 0-31.014575 13.606575-31.014575 30.299178v221.548712c0 16.776767 13.915178 30.383342 31.014575 30.383342h322.700274c17.169534 0 31.028603-13.606575 31.028603-30.383342V461.459288c0.070137-16.692603-13.859068-30.299178-31.028603-30.299178zM531.736548 578.026959v48.352438a4.067945 4.067945 0 0 1-4.081973 3.983781h-26.694137a4.067945 4.067945 0 0 1-4.067945-3.983781v-48.352438c-12.568548-6.256219-21.251507-18.852822-21.251507-33.52548 0-20.830685 17.295781-37.789808 38.673535-37.789808 21.363726 0 38.659507 16.959123 38.659506 37.775781 0 14.686685-8.668932 27.283288-21.237479 33.539507z m60.03726-146.866849H436.841205V352.227945c0-41.773589 34.787945-75.747945 77.529425-75.747945 42.755507 0 77.473315 33.974356 77.473315 75.747945l-0.070137 78.90411z" p-id="1526"></path></svg>
src/router/modules/system.ts
@@ -20,7 +20,7 @@
      path: 'user',
      component: () => import('@/views/system/user/index.vue'),
      name: 'UserManage',
      meta: { title: '用户管理', icon: 'people', noCache: false }
      meta: { title: '用户管理', icon: 'people-hdw', noCache: false }
    }
  ]
};
src/utils/resize.js
New file
@@ -0,0 +1,56 @@
import { defineComponent } from 'vue';
import { debounce } from '@/utils';
export default defineComponent({
  data() {
    return {
      sidebarElm: null,
      resizeHandler: null
    };
  },
  mounted() {
    this.resizeHandler = debounce(() => {
      if (this.chart) {
        this.chart.resize();
      }
    }, 100);
    this.initResizeEvent();
    this.initSidebarResizeEvent();
  },
  beforeUnmount() {
    this.destroyResizeEvent();
    this.destroySidebarResizeEvent();
  },
  // to fixed bug when cached by keep-alive
  // https://github.com/PanJiaChen/vue-element-admin/issues/2116
  activated() {
    this.initResizeEvent();
    this.initSidebarResizeEvent();
  },
  deactivated() {
    this.destroyResizeEvent();
    this.destroySidebarResizeEvent();
  },
  methods: {
    // do not use $_ for mixins properties
    // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
    initResizeEvent() {
      window.addEventListener('resize', this.resizeHandler);
    },
    destroyResizeEvent() {
      window.removeEventListener('resize', this.resizeHandler);
    },
    sidebarResizeHandler(e) {
      if (e.propertyName === 'width') {
        this.resizeHandler();
      }
    },
    initSidebarResizeEvent() {
      this.sidebarElm = document.getElementsByClassName('sidebar-container')[0];
      this.sidebarElm && this.sidebarElm.addEventListener('transitionend', this.sidebarResizeHandler);
    },
    destroySidebarResizeEvent() {
      this.sidebarElm && this.sidebarElm.removeEventListener('transitionend', this.sidebarResizeHandler);
    }
  }
});
src/views/dashboard/components/LineChart.vue
New file
@@ -0,0 +1,119 @@
<template>
  <div :class="className" :style="{ height: height, width: width }" />
</template>
<script>
import * as echarts from 'echarts';
import { defineComponent, shallowRef } from 'vue';
import resize from '@/utils/resize';
export default defineComponent({
  name: 'LineChart',
  mixins: [resize],
  props: {
    className: {
      type: String,
      default: 'chart'
    },
    width: {
      type: String,
      default: '100%'
    },
    height: {
      type: String,
      default: '100%'
    },
    autoResize: {
      type: Boolean,
      default: true
    },
    chartData: {
      type: Object,
      required: true
    }
  },
  data() {
    return {
      chart: null
    };
  },
  watch: {
    chartData: {
      deep: true,
      handler(val) {
        this.setOptions(val);
      }
    }
  },
  mounted() {
    this.$nextTick(() => {
      this.initChart();
    });
  },
  beforeUnmount() {
    if (!this.chart) {
      return;
    }
    this.chart.dispose();
    this.chart = null;
  },
  methods: {
    initChart() {
      this.chart = shallowRef(echarts.init(this.$el));
      this.setOptions(this.chartData);
    },
    setOptions() {
      this.chart.setOption({
        grid: {
          top: 20,
          right: 20,
          bottom: 20,
          left: 20,
          containLabel: true
        },
        xAxis: {
          type: 'category',
          boundaryGap: false,
          splitLine: {
            show: true,
            lineStyle: {
              width: 1
            }
          }
        },
        yAxis: {
          type: 'value',
          axisLine: {
            show: true
          },
          splitLine: {
            show: true,
            lineStyle: {
              width: 1,
              type: 'dashed'
            }
          }
        },
        series: [
          {
            type: 'line',
            smooth: 0.6,
            symbol: 'none',
            data: [
              ['2019-10-10', 2],
              ['2019-10-11', 5],
              ['2019-10-12', 7],
              ['2019-10-13', 5],
              ['2019-10-14', 2],
              ['2019-10-15', 3],
              ['2019-10-16', 4],
              ['2019-10-17', 3],
              ['2019-10-18', 1]
            ]
          }
        ]
      });
    }
  }
});
</script>
src/views/dashboard/components/MenuCard.vue
New file
@@ -0,0 +1,56 @@
<script lang="ts">
import { defineComponent } from 'vue';
import SvgIcon from '@/components/SvgIcon/index.vue';
export default defineComponent({
  name: 'MenuCard',
  components: { SvgIcon },
  props: {
    title: {
      type: String,
      default: '区域管理'
    },
    icon: {
      type: String,
      default: 'tree'
    },
    url: {
      type: String,
      default: '/system/region'
    }
  },
  data() {
    return {};
  },
  methods: {
    changeRouter() {
      this.$router.push(this.url);
    }
  }
});
</script>
<template>
  <div class="menu-card" @click.prevent="changeRouter">
    <svg-icon style="font-size: 40px;" :icon-class="icon"></svg-icon>
    <br />
    <br />
    <span class="menu-title">{{ title }}</span>
  </div>
</template>
<style scoped lang="scss">
.menu-card {
  display: inline-block;
  color: #F6F6F6;
  background-color: #9f80f5;
  text-align: center;
  min-width: 160px;
  padding: 8px 16px;
  border-radius: 4px;
  cursor: pointer;
  .menu-title {
    font-weight: 700;
  }
}
</style>
src/views/dashboard/index.vue
@@ -1,18 +1,307 @@
<template>
  <div class="dashboard-container"></div>
  <div class="dashboard-container">
    <hdw-card>
      <div class="data-stats-header">
        <el-row :gutter="16">
          <el-col :span="8">
            <div class="user-info-wrapper">
              <el-avatar class="user-icon" :size="30" :src="userImg" />
              <span class="user-name">武汉源畅</span>
              <span class="user-change">切换</span>
            </div>
          </el-col>
          <el-col :span="8">
            <div class="data-stats-time-wrapper">
              <span class="time-title">统计时间:</span>
              <el-radio-group class="radio-class" v-model="timeType" size="small">
                <el-radio-button label="本周" :value="0" />
                <el-radio-button label="本月" :value="1" />
                <el-radio-button label="本年" :value="2" />
              </el-radio-group>
            </div>
          </el-col>
          <el-col :span="8">
            <div class="data-stats-time-wrapper">
              <span class="time-title">自定义时间:</span>
              <el-date-picker
                  v-model="rangeTime"
                  type="daterange"
                  range-separator="至"
                  start-placeholder="开始时间"
                  end-placeholder="结束时间"
                  size="small"
              />
            </div>
          </el-col>
        </el-row>
      </div>
    </hdw-card>
    <div class="data-stats">
      <el-row :gutter="8">
        <el-col :span="8">
          <hdw-card>
            <div class="data-stats-wrapper">
              <div class="data-stats-icon">
                <div class="data-stats-icon-content">
                  <svg-icon icon-class="lock-hdw"/>
                  <br/>
                  <span class="icon-text">锁具统计</span>
                </div>
              </div>
              <div class="data-stats-content">
                <el-row :gutter="16">
                  <el-col :span="12">
                    <div class="num-stats-wrapper">
                      <div class="num-title">已安装</div>
                      <div class="num-value">112</div>
                    </div>
                  </el-col>
                  <el-col :span="12">
                    <div class="num-stats-wrapper">
                      <div class="num-title">未安装</div>
                      <div class="num-value">2</div>
                    </div>
                  </el-col>
                  <el-col :span="12">
                    <div class="num-stats-wrapper">
                      <div class="num-title">蓝牙锁</div>
                      <div class="num-value">112</div>
                    </div>
                  </el-col>
                  <el-col :span="12">
                    <div class="num-stats-wrapper">
                      <div class="num-title">无源锁</div>
                      <div class="num-value">2</div>
                    </div>
                  </el-col>
                </el-row>
              </div>
            </div>
          </hdw-card>
        </el-col>
        <el-col :span="8">
          <hdw-card>
            <div class="data-stats-wrapper">
              <div class="data-stats-icon">
                <div class="data-stats-icon-content">
                  <svg-icon icon-class="key-hdw"/>
                  <br/>
                  <span class="icon-text">钥匙统计</span>
                </div>
              </div>
              <div class="data-stats-content">
                <el-row :gutter="16">
                  <el-col :span="12">
                    <div class="num-stats-wrapper">
                      <div class="num-title">实体钥匙</div>
                      <div class="num-value">0</div>
                    </div>
                  </el-col>
                  <el-col :span="12">
                    <div class="num-stats-wrapper">
                      <div class="num-title">设备钥匙</div>
                      <div class="num-value">0</div>
                    </div>
                  </el-col>
                  <el-col :span="12">
                    <div class="num-stats-wrapper">
                      <div class="num-title">蓝牙钥匙</div>
                      <div class="num-value">6</div>
                    </div>
                  </el-col>
                </el-row>
              </div>
            </div>
          </hdw-card>
        </el-col>
        <el-col :span="8">
          <hdw-card is-full>
            <div class="data-stats-wrapper" style="height: 100%">
              <div class="data-stats-icon">
                <div class="data-stats-icon-content">
                  <svg-icon icon-class="safe-lock-hdw"/>
                  <br/>
                  <span class="icon-text">事件统计</span>
                </div>
              </div>
              <div class="data-stats-content">
                <el-row :gutter="16">
                  <el-col :span="12">
                    <div class="num-stats-wrapper">
                      <div class="num-title">开锁操作</div>
                      <div class="num-value">0</div>
                    </div>
                  </el-col>
                  <el-col :span="12">
                    <div class="num-stats-wrapper">
                      <div class="num-title">报警事件</div>
                      <div class="num-value">0</div>
                    </div>
                  </el-col>
                </el-row>
              </div>
            </div>
          </hdw-card>
        </el-col>
      </el-row>
    </div>
    <div class="data-stats-footer">
      <el-row :gutter="8" style="height: 100%">
        <el-col :span="12" style="height: 100%">
          <hdw-card title="开锁统计" is-full>
            <div class="chart-wrapper">
              <line-chart :chart-data="unlockingData"></line-chart>
            </div>
          </hdw-card>
        </el-col>
        <el-col :span="12" style="height: 100%">
          <hdw-card title="快捷入口" is-full>
            <div class="fast-menu">
              <el-row :gutter="16">
                <el-col :span="6">
                  <menu-card></menu-card>
                </el-col>
                <el-col :span="6">
                  <menu-card url="/system/user" title="用户管理" icon="people-hdw" style="background-color: #56e0c7"></menu-card>
                </el-col>
                <el-col :span="6">
                  <menu-card url="/device/lock" title="锁具管理" icon="lock-hdw" style="background-color: #fe945f"></menu-card>
                </el-col>
                <el-col :span="6">
                  <menu-card url="/device/key" title="钥匙管理" icon="key-hdw" style="background-color: #fdcc1c"></menu-card>
                </el-col>
                <el-col :span="6">
                  <menu-card url="/general/authorize" title="授权管理" icon="auth-hdw" style="background-color: #5a84fd"></menu-card>
                </el-col>
                <el-col :span="6">
                  <menu-card url="/general/log" title="日志管理" icon="log-hdw" style="background-color: #7997b3"></menu-card>
                </el-col>
                <el-col :span="6">
                  <menu-card url="/general/map" title="地图定位" icon="map-hdw" style="background-color: #1ab5b1"></menu-card>
                </el-col>
              </el-row>
            </div>
          </hdw-card>
        </el-col>
      </el-row>
    </div>
  </div>
</template>
<script>
import { defineComponent } from 'vue';
import MenuCard from '@/views/dashboard/components/MenuCard.vue';
import HdwCard from '@/components/HdwCard/index.vue';
import SvgIcon from '@/components/SvgIcon/index.vue';
import LineChart from './components/LineChart.vue';
import userImg from '@/assets/images/user.png';
export default defineComponent({
  name: 'Dashboard'
  name: 'Dashboard',
  components: { LineChart, SvgIcon, HdwCard, MenuCard },
  data() {
    return {
      unlockingData: [],
      userImg,
      timeType: 0,
      rangeTime: ''
    };
  }
});
</script>
<style scoped>
<style scoped lang="scss">
.dashboard-container {
  height: 100%;
  overflow: auto;
  padding: 8px;
  .data-stats-footer {
    min-height: 300px;
    height: calc(100% - 265px);
  }
}
.user-info-wrapper {
  display: inline-block;
  .user-icon {
    vertical-align: middle;
  }
  .user-name {
    margin-left: 8px;
    font-size: 14px;
  }
  .user-change {
    margin-left: 8px;
    font-size: 14px;
    color: #409eff;
    font-weight: 700;
    cursor: pointer;
  }
}
.data-stats-time-wrapper {
  .radio-class {
    vertical-align: middle;
  }
  .time-title {
    margin-right: 8px;
    font-size: 14px;
    display: inline-block;
  }
}
.data-stats {
  margin-top: 8px;
  padding-bottom: 8px;
}
.data-stats-wrapper {
  display: flex;
  flex-direction: row;
  padding: 16px;
  .data-stats-icon {
    display: flex;
    justify-content: center;
    align-items: center;
    text-align: center;
    flex-direction: column;
    .svg-icon {
      font-size: 100px;
      color: #5e5e5e;
    }
    .icon-text {
      display: inline-block;
      margin-top: 8px;
      font-size: 14px;
      font-weight: 700;
      color: #5e5e5e;
    }
  }
  .data-stats-content {
    flex: 1;
  }
;
}
.num-stats-wrapper {
  padding: 12px;
  text-align: center;
  .num-title {
    color: gray;
    font-size: 14px;
  }
  .num-value {
    font-size: 30px;
    font-weight: 700;
  }
}
.fast-menu {
  text-align: center;
  .el-col {
    padding: 32px 0;
  }
}
.chart-wrapper {
  height: 100%;
  overflow: hidden;
}
</style>
src/views/device/lock/index.vue
@@ -1,15 +1,163 @@
<script lang="ts">
import { defineComponent } from 'vue';
import HdwCard from '@/components/HdwCard/index.vue';
export default defineComponent({
  name: 'LockManage'
  name: 'LockManage',
  components: { HdwCard },
  data() {
    return {
      tableData: [
        {
          type: '蓝牙',
          lockName: '机柜蓝牙锁3',
          associatedArea: '武汉源畅科技',
          installState: '未安装'
        }
      ],
      page: {
        currentPage: 1,
        pageSize: 20,
        total: 0
      }
    };
  },
  methods: {
  }
});
</script>
<script lang="ts" setup>
import {
  Search,
  Plus
} from '@element-plus/icons-vue';
import HdwTree from '@/components/HdwTree/index.vue';
</script>
<template>
  <div>锁具管理</div>
  <div class="lock-wrapper">
    <div class="lock-header">
      <div class="hdw-card-container">
        <hdw-card title="区域列表" is-full>
          <hdw-tree></hdw-tree>
        </hdw-card>
      </div>
    </div>
    <div class="lock-content">
      <hdw-card is-full>
        <div class="lock-content-wrapper">
          <div class="lock-content-tools">
            <el-button type="primary" size="small" :icon="Search">查询</el-button>
            <el-button type="success" size="small" :icon="Plus">添加</el-button>
            <el-button type="success" size="small" :icon="Plus">批量添加</el-button>
            <div class="tools-filter">
              <div class="tools-filter-item">
                <div class="filter-label"></div>
                <div class="filter-content"></div>
              </div>
            </div>
          </div>
          <div class="lock-content-table">
            <div class="pos-rel">
              <div class="pos-abs">
                <el-table :data="tableData" border style="width: 100%; height: 100%">
                  <el-table-column type="index" width="50" />
                  <el-table-column prop="type" label="类型" width="180" />
                  <el-table-column prop="lockName" label="锁具名称" width="180" />
                  <el-table-column prop="associatedArea" label="关联区域" width="180" />
                  <el-table-column prop="installState" label="安装状态" width="180" />
                  <el-table-column prop="lockImg" label="锁具图片" width="180" />
                  <el-table-column prop="address" label="地址" />
                  <el-table-column align="center" fixed="right" label="操作" width="180">
                    <template #default>
                      <el-button type="primary" size="small">编辑</el-button>
                      <el-button type="danger" size="small">删除</el-button>
                    </template>
                  </el-table-column>
                </el-table>
              </div>
            </div>
          </div>
          <div class="lock-content-page">
            <div class="page-tool"></div>
            <div class="el-page-container">
              <el-pagination
                  v-model:current-page="page.currentPage"
                  v-model:page-size="page.pageSize"
                  :page-sizes="[20, 40, 60, 80, 100, 200, 300, 400]"
                  size="small"
                  :disabled="disabled"
                  :background="background"
                  layout="total, sizes, prev, pager, next, jumper"
                  :total="page.total"
                  @size-change="handleSizeChange"
                  @current-change="handleCurrentChange"
              />
            </div>
            <div class="page-tool"></div>
          </div>
        </div>
      </hdw-card>
    </div>
    <div class="lock-footer"></div>
  </div>
</template>
<style scoped lang="scss">
.lock-wrapper {
  display: flex;
  flex-direction: row;
  padding: 8px;
  height: 100%;
  .lock-content {
    flex: 1;
  }
}
.lock-content-wrapper {
  display: flex;
  flex-direction: column;
  height: 100%;
  .lock-content-tools {
    padding-bottom: 8px;
  }
  .lock-content-table {
    flex: 1;
    margin-left: -8px;
    margin-right: -8px;
  }
  .lock-content-page {
    padding: 8px 8px 0 8px;
    text-align: center;
    .el-page-container {
      display: inline-block;
      padding: 0 16px;
    }
    .page-tool {
      display: inline-block;
    }
  }
}
.hdw-card-container {
  width: 320px;
  padding-right: 8px;
  height: 100%;
}
.pos-rel {
  position: relative;
  width: 100%;
  height: 100%;
  .pos-abs {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    width: 100%;
    height: 100%;
  }
}
</style>
vite.config.ts
@@ -24,6 +24,9 @@
  return {
    base: '/', // 注意,必须以"/"结尾,BASE_URL配置
    build: {
      outDir: '../hm-lock-sys/public'
    },
    define: {
      'process.env': env
    },