summaryrefslogtreecommitdiff
path: root/UI source code/dns_mapping_ui-master/src/views
diff options
context:
space:
mode:
Diffstat (limited to 'UI source code/dns_mapping_ui-master/src/views')
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/components/Echarts.vue19
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/components/Editor.vue74
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/components/MarkDown.vue49
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/components/YamlEdit.vue207
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/components/excel/upload-excel.vue41
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/components/icons/element-icons.js74
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/components/icons/index.vue97
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/components/icons/svg-icons.js10
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/dashboard/LineChart.vue135
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/dashboard/PanelGroup.vue181
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/dashboard/mixins/resize.js55
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/features/401.vue89
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/features/404.vue225
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/features/redirect.vue12
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/generator/config.vue325
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/generator/index.vue114
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/generator/preview.vue30
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/home.vue1074
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/login.vue215
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/mnt/app/index.vue144
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/mnt/database/execute.vue86
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/mnt/database/index.vue148
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/mnt/deploy/deploy.vue190
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/mnt/deploy/index.vue229
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/mnt/deploy/sysRestore.vue108
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/mnt/deployHistory/index.vue93
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/mnt/server/index.vue136
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/monitor/log/errorLog.vue135
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/monitor/log/index.vue114
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/monitor/log/search.vue24
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/monitor/online/index.vue121
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/monitor/server/index.vue291
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/monitor/sql/index.vue16
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/nested/menu1/menu1-1/index.vue36
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/nested/menu1/menu1-2/index.vue5
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/nested/menu2/index.vue5
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/system/dept/index.vue254
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/system/dict/dictDetail.vue115
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/system/dict/index.vue135
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/system/job/index.vue110
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/system/job/module/form.vue110
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/system/job/module/header.vue32
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/system/menu/index.vue252
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/system/role/index.vue360
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/system/timing/index.vue210
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/system/timing/log.vue104
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/system/user/center.vue221
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/system/user/center/updateEmail.vue137
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/system/user/center/updatePass.vue95
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/system/user/index.vue484
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/tools/aliPay/config.vue98
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/tools/aliPay/index.vue48
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/tools/aliPay/toPay.vue86
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/tools/email/config.vue91
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/tools/email/index.vue41
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/tools/email/send.vue98
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/tools/storage/index.vue36
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/tools/storage/local/index.vue184
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/tools/storage/qiniu/form.vue98
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/tools/storage/qiniu/index.vue189
-rw-r--r--UI source code/dns_mapping_ui-master/src/views/tools/swagger/index.vue16
61 files changed, 8511 insertions, 0 deletions
diff --git a/UI source code/dns_mapping_ui-master/src/views/components/Echarts.vue b/UI source code/dns_mapping_ui-master/src/views/components/Echarts.vue
new file mode 100644
index 0000000..86533c3
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/components/Echarts.vue
@@ -0,0 +1,19 @@
+<template>
+ <div class="dashboard-container">
+
+ </div>
+</template>
+
+<script>
+
+
+
+
+export default {
+
+}
+</script>
+
+<style rel="stylesheet/scss" lang="scss" scoped>
+
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/components/Editor.vue b/UI source code/dns_mapping_ui-master/src/views/components/Editor.vue
new file mode 100644
index 0000000..ced461c
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/components/Editor.vue
@@ -0,0 +1,74 @@
+<template>
+ <div class="app-container">
+ <p class="warn-content">
+ 富文本基于
+ <el-link type="primary" href="https://www.kancloud.cn/wangfupeng/wangeditor3/332599" target="_blank">wangEditor</el-link>
+ </p>
+ <el-row :gutter="10">
+ <el-col :xs="24" :sm="24" :md="15" :lg="15" :xl="15">
+ <div ref="editor" class="text" />
+ </el-col>
+ <el-col :xs="24" :sm="24" :md="9" :lg="9" :xl="9">
+ <div v-html="editorContent" />
+ </el-col>
+ </el-row>
+ </div>
+</template>
+
+<script>
+import { mapGetters } from 'vuex'
+import { upload } from '@/utils/upload'
+import E from 'wangeditor'
+export default {
+ name: 'Editor',
+ data() {
+ return {
+ editorContent:
+ `
+ <ul>
+ <li>更多帮助请查看官方文档:<a style="color: #42b983" target="_blank" href="https://www.wangeditor.com/doc/">wangEditor</a></li>
+ </ul>
+ `
+ }
+ },
+ computed: {
+ ...mapGetters([
+ 'imagesUploadApi',
+ 'baseApi'
+ ])
+ },
+ mounted() {
+ const _this = this
+ var editor = new E(this.$refs.editor)
+ // 自定义菜单配置
+ editor.config.zIndex = 5
+ // 文件上传
+ editor.config.customUploadImg = function(files, insert) {
+ // files 是 input 中选中的文件列表
+ // insert 是获取图片 url 后,插入到编辑器的方法
+ files.forEach(image => {
+ upload(_this.imagesUploadApi, image).then(res => {
+ const data = res.data
+ const url = _this.baseApi + '/file/' + data.type + '/' + data.realName
+ insert(url)
+ })
+ })
+ }
+ editor.config.onchange = (html) => {
+ this.editorContent = html
+ }
+ editor.create()
+ // 初始化数据
+ editor.txt.html(this.editorContent)
+ }
+}
+</script>
+
+<style scoped>
+ .text {
+ text-align:left;
+ }
+ ::v-deep .w-e-text-container {
+ height: 420px !important;
+ }
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/components/MarkDown.vue b/UI source code/dns_mapping_ui-master/src/views/components/MarkDown.vue
new file mode 100644
index 0000000..bb5a10e
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/components/MarkDown.vue
@@ -0,0 +1,49 @@
+<template>
+ <div class="app-container">
+ <p class="warn-content">
+ Markdown 基于
+ <el-link type="primary" href="https://github.com/hinesboy/mavonEditor" target="_blank">MavonEditor</el-link>
+ </p>
+ <mavon-editor ref="md" :style="'height:' + height" @imgAdd="imgAdd" />
+ </div>
+</template>
+
+<script>
+import { upload } from '@/utils/upload'
+import { mapGetters } from 'vuex'
+export default {
+ name: 'Markdown',
+ data() {
+ return {
+ height: document.documentElement.clientHeight - 200 + 'px'
+ }
+ },
+ computed: {
+ ...mapGetters([
+ 'imagesUploadApi',
+ 'baseApi'
+ ])
+ },
+ mounted() {
+ const that = this
+ window.onresize = function temp() {
+ that.height = document.documentElement.clientHeight - 200 + 'px'
+ }
+ },
+ methods: {
+ imgAdd(pos, $file) {
+ upload(this.imagesUploadApi, $file).then(res => {
+ const data = res.data
+ const url = this.baseApi + '/file/' + data.type + '/' + data.realName
+ this.$refs.md.$img2Url(pos, url)
+ })
+ }
+ }
+}
+</script>
+
+<style scoped>
+ .v-note-wrapper.shadow {
+ z-index: 5;
+ }
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/components/YamlEdit.vue b/UI source code/dns_mapping_ui-master/src/views/components/YamlEdit.vue
new file mode 100644
index 0000000..9455fde
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/components/YamlEdit.vue
@@ -0,0 +1,207 @@
+<template>
+ <div class="app-container">
+ <p class="warn-content">
+ Yaml编辑器 基于
+ <a href="https://github.com/codemirror/CodeMirror" target="_blank">CodeMirror</a>,
+ 主题预览地址 <a href="https://codemirror.net/demo/theme.html#idea" target="_blank">Theme</a>
+ </p>
+ <Yaml :value="value" :height="height" />
+ </div>
+</template>
+
+<script>
+import Yaml from '@/components/YamlEdit/index'
+export default {
+ name: 'YamlEdit',
+ components: { Yaml },
+ data() {
+ return {
+ height: document.documentElement.clientHeight - 210 + 'px',
+ value: '# 展示数据,如需更换主题,请在src/components/YamlEdit 目录中搜索原主题名称进行替换\n' +
+ '\n' +
+ '# ===================================================================\n' +
+ '# Spring Boot configuration.\n' +
+ '#\n' +
+ '# This configuration will be overridden by the Spring profile you use,\n' +
+ '# for example application-dev.yml if you use the "dev" profile.\n' +
+ '#\n' +
+ '# More information on profiles: https://www.jhipster.tech/profiles/\n' +
+ '# More information on configuration properties: https://www.jhipster.tech/common-application-properties/\n' +
+ '# ===================================================================\n' +
+ '\n' +
+ '# ===================================================================\n' +
+ '# Standard Spring Boot properties.\n' +
+ '# Full reference is available at:\n' +
+ '# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html\n' +
+ '# ===================================================================\n' +
+ '\n' +
+ 'eureka:\n' +
+ ' client:\n' +
+ ' enabled: true\n' +
+ ' healthcheck:\n' +
+ ' enabled: true\n' +
+ ' fetch-registry: true\n' +
+ ' register-with-eureka: true\n' +
+ ' instance-info-replication-interval-seconds: 10\n' +
+ ' registry-fetch-interval-seconds: 10\n' +
+ ' instance:\n' +
+ ' appname: product\n' +
+ ' instanceId: product:${spring.application.instance-id:${random.value}}\n' +
+ ' #instanceId: 127.0.0.1:9080\n' +
+ ' lease-renewal-interval-in-seconds: 5\n' +
+ ' lease-expiration-duration-in-seconds: 10\n' +
+ ' status-page-url-path: ${management.endpoints.web.base-path}/info\n' +
+ ' health-check-url-path: ${management.endpoints.web.base-path}/health\n' +
+ ' metadata-map:\n' +
+ ' zone: primary # This is needed for the load balancer\n' +
+ ' profile: ${spring.profiles.active}\n' +
+ ' version: ${info.project.version:}\n' +
+ ' git-version: ${git.commit.id.describe:}\n' +
+ ' git-commit: ${git.commit.id.abbrev:}\n' +
+ ' git-branch: ${git.branch:}\n' +
+ 'ribbon:\n' +
+ ' ReadTimeout: 120000\n' +
+ ' ConnectTimeout: 300000\n' +
+ ' eureka:\n' +
+ ' enabled: true\n' +
+ 'zuul:\n' +
+ ' host:\n' +
+ ' connect-timeout-millis: 5000\n' +
+ ' max-per-route-connections: 10000\n' +
+ ' max-total-connections: 5000\n' +
+ ' socket-timeout-millis: 60000\n' +
+ ' semaphore:\n' +
+ ' max-semaphores: 500\n' +
+ '\n' +
+ 'feign:\n' +
+ ' hystrix:\n' +
+ ' enabled: true\n' +
+ ' client:\n' +
+ ' config:\n' +
+ ' default:\n' +
+ ' connectTimeout: 500000\n' +
+ ' readTimeout: 500000\n' +
+ '\n' +
+ '# See https://github.com/Netflix/Hystrix/wiki/Configuration\n' +
+ 'hystrix:\n' +
+ ' command:\n' +
+ ' default:\n' +
+ ' circuitBreaker:\n' +
+ ' sleepWindowInMilliseconds: 100000\n' +
+ ' forceClosed: true\n' +
+ ' execution:\n' +
+ ' isolation:\n' +
+ '# strategy: SEMAPHORE\n' +
+ '# See https://github.com/spring-cloud/spring-cloud-netflix/issues/1330\n' +
+ ' thread:\n' +
+ ' timeoutInMilliseconds: 60000\n' +
+ ' shareSecurityContext: true\n' +
+ '\n' +
+ 'management:\n' +
+ ' endpoints:\n' +
+ ' web:\n' +
+ ' base-path: /management\n' +
+ ' exposure:\n' +
+ ' include: ["configprops", "env", "health", "info", "threaddump"]\n' +
+ ' endpoint:\n' +
+ ' health:\n' +
+ ' show-details: when_authorized\n' +
+ ' info:\n' +
+ ' git:\n' +
+ ' mode: full\n' +
+ ' health:\n' +
+ ' mail:\n' +
+ ' enabled: false # When using the MailService, configure an SMTP server and set this to true\n' +
+ ' metrics:\n' +
+ ' enabled: false # http://micrometer.io/ is disabled by default, as we use http://metrics.dropwizard.io/ instead\n' +
+ '\n' +
+ 'spring:\n' +
+ ' application:\n' +
+ ' name: product\n' +
+ ' jpa:\n' +
+ ' open-in-view: false\n' +
+ ' hibernate:\n' +
+ ' ddl-auto: update\n' +
+ ' naming:\n' +
+ ' physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy\n' +
+ ' implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy\n' +
+ ' messages:\n' +
+ ' basename: i18n/messages\n' +
+ ' mvc:\n' +
+ ' favicon:\n' +
+ ' enabled: false\n' +
+ ' thymeleaf:\n' +
+ ' mode: HTML\n' +
+ 'security:\n' +
+ ' oauth2:\n' +
+ ' resource:\n' +
+ ' filter-order: 3\n' +
+ '\n' +
+ 'server:\n' +
+ ' servlet:\n' +
+ ' session:\n' +
+ ' cookie:\n' +
+ ' http-only: true\n' +
+ '\n' +
+ '# Properties to be exposed on the /info management endpoint\n' +
+ 'info:\n' +
+ ' # Comma separated list of profiles that will trigger the ribbon to show\n' +
+ ' display-ribbon-on-profiles: "dev"\n' +
+ '\n' +
+ '# ===================================================================\n' +
+ '# JHipster specific properties\n' +
+ '#\n' +
+ '# Full reference is available at: https://www.jhipster.tech/common-application-properties/\n' +
+ '# ===================================================================\n' +
+ '\n' +
+ 'jhipster:\n' +
+ ' async:\n' +
+ ' core-pool-size: 2\n' +
+ ' max-pool-size: 50\n' +
+ ' queue-capacity: 10000\n' +
+ ' # By default CORS is disabled. Uncomment to enable.\n' +
+ ' #cors:\n' +
+ ' #allowed-origins: "*"\n' +
+ ' #allowed-methods: "*"\n' +
+ ' #allowed-headers: "*"\n' +
+ ' #exposed-headers: "Authorization,Link,X-Total-Count"\n' +
+ ' #allow-credentials: true\n' +
+ ' #max-age: 1800\n' +
+ ' mail:\n' +
+ ' from: product@localhost\n' +
+ ' swagger:\n' +
+ ' default-include-pattern: /api/.*\n' +
+ ' title: product API\n' +
+ ' description: product API documentation\n' +
+ ' version: 0.0.1\n' +
+ ' terms-of-service-url:\n' +
+ ' contact-name:\n' +
+ ' contact-url:\n' +
+ ' contact-email:\n' +
+ ' license:\n' +
+ ' license-url:\n' +
+ '\n' +
+ '# ===================================================================\n' +
+ '# Application specific properties\n' +
+ '# Add your own application properties here, see the ApplicationProperties class\n' +
+ '# to have type-safe configuration, like in the JHipsterProperties above\n' +
+ '#\n' +
+ '# More documentation is available at:\n' +
+ '# https://www.jhipster.tech/common-application-properties/\n' +
+ '# ===================================================================\n' +
+ '\n' +
+ '# application:\n'
+ }
+ },
+ mounted() {
+ const that = this
+ window.onresize = function temp() {
+ that.height = document.documentElement.clientHeight - 210 + 'px'
+ }
+ }
+}
+</script>
+
+<style scoped>
+
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/components/excel/upload-excel.vue b/UI source code/dns_mapping_ui-master/src/views/components/excel/upload-excel.vue
new file mode 100644
index 0000000..75f7634
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/components/excel/upload-excel.vue
@@ -0,0 +1,41 @@
+<template>
+ <div class="app-container">
+ <upload-excel-component :on-success="handleSuccess" :before-upload="beforeUpload" />
+ <el-table :data="tableData" border highlight-current-row style="width: 100%;margin-top:20px;">
+ <el-table-column v-for="item of tableHeader" :key="item" :prop="item" :label="item" />
+ </el-table>
+ </div>
+</template>
+
+<script>
+import UploadExcelComponent from '@/components/UploadExcel/index.vue'
+
+export default {
+ name: 'UploadExcel',
+ components: { UploadExcelComponent },
+ data() {
+ return {
+ tableData: [],
+ tableHeader: []
+ }
+ },
+ methods: {
+ beforeUpload(file) {
+ const isLt1M = file.size / 1024 / 1024 < 1
+ if (isLt1M) {
+ return true
+ }
+
+ this.$message({
+ message: '请不要上传大于1m的文件.',
+ type: 'warning'
+ })
+ return false
+ },
+ handleSuccess({ results, header }) {
+ this.tableData = results
+ this.tableHeader = header
+ }
+ }
+}
+</script>
diff --git a/UI source code/dns_mapping_ui-master/src/views/components/icons/element-icons.js b/UI source code/dns_mapping_ui-master/src/views/components/icons/element-icons.js
new file mode 100644
index 0000000..df72201
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/components/icons/element-icons.js
@@ -0,0 +1,74 @@
+const elementIcons = [
+ 'info',
+ 'error',
+ 'success',
+ 'warning',
+ 'question',
+ 'back',
+ 'arrow-left',
+ 'arrow-down',
+ 'arrow-right',
+ 'arrow-up',
+ 'caret-left',
+ 'caret-bottom',
+ 'caret-top',
+ 'caret-right',
+ 'd-arrow-left',
+ 'd-arrow-right',
+ 'minus',
+ 'plus',
+ 'remove',
+ 'circle-plus',
+ 'remove-outline',
+ 'circle-plus-outline',
+ 'close',
+ 'check',
+ 'circle-close',
+ 'circle-check',
+ 'circle-close-outline',
+ 'circle-check-outline',
+ 'zoom-out',
+ 'zoom-in',
+ 'd-caret',
+ 'sort',
+ 'sort-down',
+ 'sort-up',
+ 'tickets',
+ 'document',
+ 'goods',
+ 'sold-out',
+ 'news',
+ 'message',
+ 'date',
+ 'printer',
+ 'time',
+ 'bell',
+ 'mobile-phone',
+ 'service',
+ 'view',
+ 'menu',
+ 'more',
+ 'more-outline',
+ 'star-on',
+ 'star-off',
+ 'location',
+ 'location-outline',
+ 'phone',
+ 'phone-outline',
+ 'picture',
+ 'picture-outline',
+ 'delete',
+ 'search',
+ 'edit',
+ 'edit-outline',
+ 'rank',
+ 'refresh',
+ 'share',
+ 'setting',
+ 'upload',
+ 'upload2',
+ 'download',
+ 'loading'
+]
+
+export default elementIcons
diff --git a/UI source code/dns_mapping_ui-master/src/views/components/icons/index.vue b/UI source code/dns_mapping_ui-master/src/views/components/icons/index.vue
new file mode 100644
index 0000000..d060173
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/components/icons/index.vue
@@ -0,0 +1,97 @@
+<template>
+ <div class="icons-container">
+ <aside>
+ <a href="https://panjiachen.github.io/vue-element-admin-site/guide/advanced/icon.html" target="_blank">Add and use
+ </a>
+ </aside>
+ <el-tabs type="border-card">
+ <el-tab-pane label="Icons">
+ <div class="grid">
+ <div v-for="item of svgIcons" :key="item" @click="handleClipboard(generateIconCode(item),$event)">
+ <el-tooltip placement="top">
+ <div slot="content">
+ {{ generateIconCode(item) }}
+ </div>
+ <div class="icon-item">
+ <svg-icon :icon-class="item" class-name="disabled" />
+ <span>{{ item }}</span>
+ </div>
+ </el-tooltip>
+ </div>
+ </div>
+ </el-tab-pane>
+ <el-tab-pane label="Element-UI Icons">
+ <div class="grid">
+ <div v-for="item of elementIcons" :key="item" @click="handleClipboard(generateElementIconCode(item),$event)">
+ <el-tooltip placement="top">
+ <div slot="content">
+ {{ generateElementIconCode(item) }}
+ </div>
+ <div class="icon-item">
+ <i :class="'el-icon-' + item" />
+ <span>{{ item }}</span>
+ </div>
+ </el-tooltip>
+ </div>
+ </div>
+ </el-tab-pane>
+ </el-tabs>
+ </div>
+</template>
+
+<script>
+import clipboard from '@/utils/clipboard'
+import svgIcons from './svg-icons'
+import elementIcons from './element-icons'
+export default {
+ name: 'Icons',
+ data() {
+ return {
+ svgIcons,
+ elementIcons
+ }
+ },
+ methods: {
+ generateIconCode(symbol) {
+ return `<svg-icon icon-class="${symbol}" />`
+ },
+ generateElementIconCode(symbol) {
+ return `<i class="el-icon-${symbol}" />`
+ },
+ handleClipboard(text, event) {
+ clipboard(text, event)
+ }
+ }
+}
+</script>
+
+<style lang="scss" scoped>
+.icons-container {
+ margin: 10px 20px 0;
+ overflow: hidden;
+
+ .grid {
+ position: relative;
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
+ }
+ .icon-item {
+ margin: 20px;
+ height: 85px;
+ text-align: center;
+ width: 100px;
+ float: left;
+ font-size: 30px;
+ color: #24292e;
+ cursor: pointer;
+ }
+ span {
+ display: block;
+ font-size: 16px;
+ margin-top: 10px;
+ }
+ .disabled {
+ pointer-events: none;
+ }
+}
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/components/icons/svg-icons.js b/UI source code/dns_mapping_ui-master/src/views/components/icons/svg-icons.js
new file mode 100644
index 0000000..724cd8e
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/components/icons/svg-icons.js
@@ -0,0 +1,10 @@
+const req = require.context('../../../assets/icons/svg', false, /\.svg$/)
+const requireAll = requireContext => requireContext.keys()
+
+const re = /\.\/(.*)\.svg/
+
+const svgIcons = requireAll(req).map(i => {
+ return i.match(re)[1]
+})
+
+export default svgIcons
diff --git a/UI source code/dns_mapping_ui-master/src/views/dashboard/LineChart.vue b/UI source code/dns_mapping_ui-master/src/views/dashboard/LineChart.vue
new file mode 100644
index 0000000..e654168
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/dashboard/LineChart.vue
@@ -0,0 +1,135 @@
+<template>
+ <div :class="className" :style="{height:height,width:width}" />
+</template>
+
+<script>
+import echarts from 'echarts'
+require('echarts/theme/macarons') // echarts theme
+import resize from './mixins/resize'
+
+export default {
+ mixins: [resize],
+ props: {
+ className: {
+ type: String,
+ default: 'chart'
+ },
+ width: {
+ type: String,
+ default: '100%'
+ },
+ height: {
+ type: String,
+ default: '350px'
+ },
+ 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()
+ })
+ },
+ beforeDestroy() {
+ if (!this.chart) {
+ return
+ }
+ this.chart.dispose()
+ this.chart = null
+ },
+ methods: {
+ initChart() {
+ this.chart = echarts.init(this.$el, 'macarons')
+ this.setOptions(this.chartData)
+ },
+ setOptions({ expectedData, actualData } = {}) {
+ this.chart.setOption({
+ xAxis: {
+ data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
+ boundaryGap: false,
+ axisTick: {
+ show: false
+ }
+ },
+ grid: {
+ left: 10,
+ right: 10,
+ bottom: 20,
+ top: 30,
+ containLabel: true
+ },
+ tooltip: {
+ trigger: 'axis',
+ axisPointer: {
+ type: 'cross'
+ },
+ padding: [5, 10]
+ },
+ yAxis: {
+ axisTick: {
+ show: false
+ }
+ },
+ legend: {
+ data: ['expected', 'actual']
+ },
+ series: [{
+ name: 'expected', itemStyle: {
+ normal: {
+ color: '#FF005A',
+ lineStyle: {
+ color: '#FF005A',
+ width: 2
+ }
+ }
+ },
+ smooth: true,
+ type: 'line',
+ data: expectedData,
+ animationDuration: 2800,
+ animationEasing: 'cubicInOut'
+ },
+ {
+ name: 'actual',
+ smooth: true,
+ type: 'line',
+ itemStyle: {
+ normal: {
+ color: '#3888fa',
+ lineStyle: {
+ color: '#3888fa',
+ width: 2
+ },
+ areaStyle: {
+ color: '#f3f8ff'
+ }
+ }
+ },
+ data: actualData,
+ animationDuration: 2800,
+ animationEasing: 'quadraticOut'
+ }]
+ })
+ }
+ }
+}
+</script>
diff --git a/UI source code/dns_mapping_ui-master/src/views/dashboard/PanelGroup.vue b/UI source code/dns_mapping_ui-master/src/views/dashboard/PanelGroup.vue
new file mode 100644
index 0000000..fe1815f
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/dashboard/PanelGroup.vue
@@ -0,0 +1,181 @@
+<template>
+ <el-row :gutter="40" class="panel-group">
+ <el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
+ <div class="card-panel" @click="handleSetLineChartData('newVisitis')">
+ <div class="card-panel-icon-wrapper icon-people">
+ <svg-icon icon-class="peoples" class-name="card-panel-icon" />
+ </div>
+ <div class="card-panel-description">
+ <div class="card-panel-text">
+ New Visits
+ </div>
+ <count-to :start-val="0" :end-val="102400" :duration="2600" class="card-panel-num" />
+ </div>
+ </div>
+ </el-col>
+ <el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
+ <div class="card-panel" @click="handleSetLineChartData('messages')">
+ <div class="card-panel-icon-wrapper icon-message">
+ <svg-icon icon-class="message" class-name="card-panel-icon" />
+ </div>
+ <div class="card-panel-description">
+ <div class="card-panel-text">
+ Messages
+ </div>
+ <count-to :start-val="0" :end-val="81212" :duration="3000" class="card-panel-num" />
+ </div>
+ </div>
+ </el-col>
+ <el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
+ <div class="card-panel" @click="handleSetLineChartData('purchases')">
+ <div class="card-panel-icon-wrapper icon-money">
+ <svg-icon icon-class="money" class-name="card-panel-icon" />
+ </div>
+ <div class="card-panel-description">
+ <div class="card-panel-text">
+ Purchases
+ </div>
+ <count-to :start-val="0" :end-val="9280" :duration="3200" class="card-panel-num" />
+ </div>
+ </div>
+ </el-col>
+ <el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
+ <div class="card-panel" @click="handleSetLineChartData('shoppings')">
+ <div class="card-panel-icon-wrapper icon-shopping">
+ <svg-icon icon-class="shopping" class-name="card-panel-icon" />
+ </div>
+ <div class="card-panel-description">
+ <div class="card-panel-text">
+ Shoppings
+ </div>
+ <count-to :start-val="0" :end-val="13600" :duration="3600" class="card-panel-num" />
+ </div>
+ </div>
+ </el-col>
+ </el-row>
+</template>
+
+<script>
+import CountTo from 'vue-count-to'
+
+export default {
+ components: {
+ CountTo
+ },
+ methods: {
+ handleSetLineChartData(type) {
+ this.$emit('handleSetLineChartData', type)
+ }
+ }
+}
+</script>
+
+<style lang="scss" scoped>
+.panel-group {
+ margin-top: 18px;
+
+ .card-panel-col {
+ margin-bottom: 32px;
+ }
+
+ .card-panel {
+ height: 108px;
+ cursor: pointer;
+ font-size: 12px;
+ position: relative;
+ overflow: hidden;
+ color: #666;
+ background: #fff;
+ box-shadow: 4px 4px 40px rgba(0, 0, 0, .05);
+ border-color: rgba(0, 0, 0, .05);
+
+ &:hover {
+ .card-panel-icon-wrapper {
+ color: #fff;
+ }
+
+ .icon-people {
+ background: #40c9c6;
+ }
+
+ .icon-message {
+ background: #36a3f7;
+ }
+
+ .icon-money {
+ background: #f4516c;
+ }
+
+ .icon-shopping {
+ background: #34bfa3
+ }
+ }
+
+ .icon-people {
+ color: #40c9c6;
+ }
+
+ .icon-message {
+ color: #36a3f7;
+ }
+
+ .icon-money {
+ color: #f4516c;
+ }
+
+ .icon-shopping {
+ color: #34bfa3
+ }
+
+ .card-panel-icon-wrapper {
+ float: left;
+ margin: 14px 0 0 14px;
+ padding: 16px;
+ transition: all 0.38s ease-out;
+ border-radius: 6px;
+ }
+
+ .card-panel-icon {
+ float: left;
+ font-size: 48px;
+ }
+
+ .card-panel-description {
+ float: right;
+ font-weight: bold;
+ margin: 26px;
+ margin-left: 0px;
+
+ .card-panel-text {
+ line-height: 18px;
+ color: rgba(0, 0, 0, 0.45);
+ font-size: 16px;
+ margin-bottom: 12px;
+ }
+
+ .card-panel-num {
+ font-size: 20px;
+ }
+ }
+ }
+}
+
+// @media (max-width:550px) {
+// .card-panel-description {
+// display: none;
+// }
+
+// .card-panel-icon-wrapper {
+// float: none !important;
+// width: 100%;
+// height: 100%;
+// margin: 0 !important;
+
+// .svg-icon {
+// display: block;
+// margin: 14px auto !important;
+// float: none !important;
+// }
+// }
+// }
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/dashboard/mixins/resize.js b/UI source code/dns_mapping_ui-master/src/views/dashboard/mixins/resize.js
new file mode 100644
index 0000000..234953b
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/dashboard/mixins/resize.js
@@ -0,0 +1,55 @@
+import { debounce } from '@/utils'
+
+export default {
+ data() {
+ return {
+ $_sidebarElm: null,
+ $_resizeHandler: null
+ }
+ },
+ mounted() {
+ this.$_resizeHandler = debounce(() => {
+ if (this.chart) {
+ this.chart.resize()
+ }
+ }, 100)
+ this.$_initResizeEvent()
+ this.$_initSidebarResizeEvent()
+ },
+ beforeDestroy() {
+ 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: {
+ // 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)
+ }
+ }
+}
diff --git a/UI source code/dns_mapping_ui-master/src/views/features/401.vue b/UI source code/dns_mapping_ui-master/src/views/features/401.vue
new file mode 100644
index 0000000..8a3b69e
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/features/401.vue
@@ -0,0 +1,89 @@
+<template>
+ <div class="errPage-container">
+ <el-button icon="arrow-left" class="pan-back-btn" @click="back">
+ 返回
+ </el-button>
+ <el-row>
+ <el-col :span="12">
+ <h1 class="text-jumbo text-ginormous">
+ Oops!
+ </h1>
+ <h2>你没有权限去该页面</h2>
+ <h6>如有不满请联系你领导</h6>
+ <ul class="list-unstyled">
+ <li>或者你可以去:</li>
+ <li class="link-type">
+ <router-link to="/dashboard">
+ 回首页
+ </router-link>
+ </li>
+ </ul>
+ </el-col>
+ <el-col :span="12">
+ <img :src="errGif" width="313" height="428" alt="Girl has dropped her ice cream.">
+ </el-col>
+ </el-row>
+ </div>
+</template>
+
+<script>
+import errGif from '@/assets/401_images/401.gif'
+
+export default {
+ name: 'Page401',
+ data() {
+ return {
+ errGif: errGif + '?' + +new Date()
+ }
+ },
+ methods: {
+ back() {
+ if (this.$route.query.noGoBack) {
+ this.$router.push({ path: '/dashboard' })
+ } else {
+ this.$router.go(-1)
+ }
+ }
+ }
+}
+</script>
+
+<style lang="scss" scoped>
+ .errPage-container {
+ width: 800px;
+ max-width: 100%;
+ margin: 100px auto;
+ .pan-back-btn {
+ background: #008489;
+ color: #fff;
+ border: none!important;
+ }
+ .pan-gif {
+ margin: 0 auto;
+ display: block;
+ }
+ .pan-img {
+ display: block;
+ margin: 0 auto;
+ width: 100%;
+ }
+ .text-jumbo {
+ font-size: 60px;
+ font-weight: 700;
+ color: #484848;
+ }
+ .list-unstyled {
+ font-size: 14px;
+ li {
+ padding-bottom: 5px;
+ }
+ a {
+ color: #008489;
+ text-decoration: none;
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+ }
+ }
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/features/404.vue b/UI source code/dns_mapping_ui-master/src/views/features/404.vue
new file mode 100644
index 0000000..237d81f
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/features/404.vue
@@ -0,0 +1,225 @@
+<template>
+ <div class="wscn-http404-container">
+ <div class="wscn-http404">
+ <div class="pic-404">
+ <img class="pic-404__parent" src="@/assets/404_images/404.png" alt="404">
+ <img class="pic-404__child left" src="@/assets/404_images/404_cloud.png" alt="404">
+ <img class="pic-404__child mid" src="@/assets/404_images/404_cloud.png" alt="404">
+ <img class="pic-404__child right" src="@/assets/404_images/404_cloud.png" alt="404">
+ </div>
+ <div class="bullshit">
+ <div class="bullshit__oops">OOPS!</div>
+ <div class="bullshit__headline">{{ message }}</div>
+ <div class="bullshit__info">请检查您输入的网址是否正确,请点击以下按钮返回主页或者发送错误报告</div>
+ <a href="/" class="bullshit__return-home">返回首页</a>
+ </div>
+ </div>
+ </div>
+</template>
+
+<script>
+
+export default {
+ name: 'Page404',
+ computed: {
+ message() {
+ return '网管说这个页面你不能进......'
+ }
+ }
+}
+</script>
+
+<style rel="stylesheet/scss" lang="scss" scoped>
+.wscn-http404-container{
+ transform: translate(-50%,-50%);
+ position: absolute;
+ top: 40%;
+ left: 50%;
+}
+.wscn-http404 {
+ position: relative;
+ width: 1200px;
+ padding: 0 50px;
+ overflow: hidden;
+ .pic-404 {
+ position: relative;
+ float: left;
+ width: 600px;
+ overflow: hidden;
+ &__parent {
+ width: 100%;
+ }
+ &__child {
+ position: absolute;
+ &.left {
+ width: 80px;
+ top: 17px;
+ left: 220px;
+ opacity: 0;
+ animation-name: cloudLeft;
+ animation-duration: 2s;
+ animation-timing-function: linear;
+ animation-fill-mode: forwards;
+ animation-delay: 1s;
+ }
+ &.mid {
+ width: 46px;
+ top: 10px;
+ left: 420px;
+ opacity: 0;
+ animation-name: cloudMid;
+ animation-duration: 2s;
+ animation-timing-function: linear;
+ animation-fill-mode: forwards;
+ animation-delay: 1.2s;
+ }
+ &.right {
+ width: 62px;
+ top: 100px;
+ left: 500px;
+ opacity: 0;
+ animation-name: cloudRight;
+ animation-duration: 2s;
+ animation-timing-function: linear;
+ animation-fill-mode: forwards;
+ animation-delay: 1s;
+ }
+ @keyframes cloudLeft {
+ 0% {
+ top: 17px;
+ left: 220px;
+ opacity: 0;
+ }
+ 20% {
+ top: 33px;
+ left: 188px;
+ opacity: 1;
+ }
+ 80% {
+ top: 81px;
+ left: 92px;
+ opacity: 1;
+ }
+ 100% {
+ top: 97px;
+ left: 60px;
+ opacity: 0;
+ }
+ }
+ @keyframes cloudMid {
+ 0% {
+ top: 10px;
+ left: 420px;
+ opacity: 0;
+ }
+ 20% {
+ top: 40px;
+ left: 360px;
+ opacity: 1;
+ }
+ 70% {
+ top: 130px;
+ left: 180px;
+ opacity: 1;
+ }
+ 100% {
+ top: 160px;
+ left: 120px;
+ opacity: 0;
+ }
+ }
+ @keyframes cloudRight {
+ 0% {
+ top: 100px;
+ left: 500px;
+ opacity: 0;
+ }
+ 20% {
+ top: 120px;
+ left: 460px;
+ opacity: 1;
+ }
+ 80% {
+ top: 180px;
+ left: 340px;
+ opacity: 1;
+ }
+ 100% {
+ top: 200px;
+ left: 300px;
+ opacity: 0;
+ }
+ }
+ }
+ }
+ .bullshit {
+ position: relative;
+ float: left;
+ width: 300px;
+ padding: 30px 0;
+ overflow: hidden;
+ &__oops {
+ font-size: 32px;
+ font-weight: bold;
+ line-height: 40px;
+ color: #1482f0;
+ opacity: 0;
+ margin-bottom: 20px;
+ animation-name: slideUp;
+ animation-duration: 0.5s;
+ animation-fill-mode: forwards;
+ }
+ &__headline {
+ font-size: 20px;
+ line-height: 24px;
+ color: #222;
+ font-weight: bold;
+ opacity: 0;
+ margin-bottom: 10px;
+ animation-name: slideUp;
+ animation-duration: 0.5s;
+ animation-delay: 0.1s;
+ animation-fill-mode: forwards;
+ }
+ &__info {
+ font-size: 13px;
+ line-height: 21px;
+ color: grey;
+ opacity: 0;
+ margin-bottom: 30px;
+ animation-name: slideUp;
+ animation-duration: 0.5s;
+ animation-delay: 0.2s;
+ animation-fill-mode: forwards;
+ }
+ &__return-home {
+ display: block;
+ float: left;
+ width: 110px;
+ height: 36px;
+ background: #1482f0;
+ border-radius: 100px;
+ text-align: center;
+ color: #ffffff;
+ opacity: 0;
+ font-size: 14px;
+ line-height: 36px;
+ cursor: pointer;
+ animation-name: slideUp;
+ animation-duration: 0.5s;
+ animation-delay: 0.3s;
+ animation-fill-mode: forwards;
+ }
+ @keyframes slideUp {
+ 0% {
+ transform: translateY(60px);
+ opacity: 0;
+ }
+ 100% {
+ transform: translateY(0);
+ opacity: 1;
+ }
+ }
+ }
+}
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/features/redirect.vue b/UI source code/dns_mapping_ui-master/src/views/features/redirect.vue
new file mode 100644
index 0000000..db4c1d6
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/features/redirect.vue
@@ -0,0 +1,12 @@
+<script>
+export default {
+ created() {
+ const { params, query } = this.$route
+ const { path } = params
+ this.$router.replace({ path: '/' + path, query })
+ },
+ render: function(h) {
+ return h() // avoid warning message
+ }
+}
+</script>
diff --git a/UI source code/dns_mapping_ui-master/src/views/generator/config.vue b/UI source code/dns_mapping_ui-master/src/views/generator/config.vue
new file mode 100644
index 0000000..2690dd5
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/generator/config.vue
@@ -0,0 +1,325 @@
+<template>
+ <div class="app-container">
+ <el-row :gutter="15">
+ <el-col style="margin-bottom: 10px">
+ <el-card class="box-card" shadow="never">
+ <div slot="header" class="clearfix">
+ <span class="role-span">字段配置:{{ tableName }}</span>
+ <el-button
+ :loading="genLoading"
+ icon="el-icon-s-promotion"
+ size="mini"
+ style="float: right; padding: 6px 9px;"
+ type="success"
+ @click="toGen"
+ >保存&生成</el-button>
+ <el-button
+ :loading="columnLoading"
+ icon="el-icon-check"
+ size="mini"
+ style="float: right; padding: 6px 9px;margin-right: 9px"
+ type="primary"
+ @click="saveColumnConfig"
+ >保存</el-button>
+ <el-tooltip class="item" effect="dark" content="数据库中表字段变动时使用该功能" placement="top-start">
+ <el-button
+ :loading="syncLoading"
+ icon="el-icon-refresh"
+ size="mini"
+ style="float: right; padding: 6px 9px;"
+ type="info"
+ @click="sync"
+ >同步</el-button>
+ </el-tooltip>
+ </div>
+ <el-form size="small" label-width="90px">
+ <el-table v-loading="loading" :data="data" :max-height="tableHeight" size="small" style="width: 100%;margin-bottom: 15px">
+ <el-table-column prop="columnName" label="字段名称" />
+ <el-table-column prop="columnType" label="字段类型" />
+ <el-table-column prop="remark" label="字段描述">
+ <template slot-scope="scope">
+ <el-input v-model="data[scope.$index].remark" size="mini" class="edit-input" />
+ </template>
+ </el-table-column>
+ <el-table-column align="center" label="必填" width="70px">
+ <template slot-scope="scope">
+ <el-checkbox v-model="data[scope.$index].notNull" />
+ </template>
+ </el-table-column>
+ <el-table-column align="center" label="列表" width="70px">
+ <template slot-scope="scope">
+ <el-checkbox v-model="data[scope.$index].listShow" />
+ </template>
+ </el-table-column>
+ <el-table-column align="center" label="表单" width="70px">
+ <template slot-scope="scope">
+ <el-checkbox v-model="data[scope.$index].formShow" />
+ </template>
+ </el-table-column>
+ <el-table-column label="表单类型">
+ <template slot-scope="scope">
+ <el-select v-model="data[scope.$index].formType" filterable class="edit-input" clearable size="mini" placeholder="请选择">
+ <el-option
+ label="文本框"
+ value="Input"
+ />
+ <el-option
+ label="文本域"
+ value="Textarea"
+ />
+ <el-option
+ label="单选框"
+ value="Radio"
+ />
+ <el-option
+ label="下拉框"
+ value="Select"
+ />
+ <el-option
+ label="日期框"
+ value="Date"
+ />
+ </el-select>
+ </template>
+ </el-table-column>
+ <el-table-column label="查询方式">
+ <template slot-scope="scope">
+ <el-select v-model="data[scope.$index].queryType" filterable class="edit-input" clearable size="mini" placeholder="请选择">
+ <el-option
+ label="="
+ value="="
+ />
+ <el-option
+ label="!="
+ value="!="
+ />
+ <el-option
+ label=">="
+ value=">="
+ />
+ <el-option
+ label="<="
+ value="<="
+ />
+ <el-option
+ label="Like"
+ value="Like"
+ />
+ <el-option
+ label="NotNull"
+ value="NotNull"
+ />
+ <el-option
+ label="BetWeen"
+ value="BetWeen"
+ />
+ </el-select>
+ </template>
+ </el-table-column>
+ <el-table-column label="日期注解">
+ <template slot-scope="scope">
+ <el-select v-model="data[scope.$index].dateAnnotation" filterable class="edit-input" clearable size="mini" placeholder="请选择">
+ <el-option
+ label="自动创建时间"
+ value="CreationTimestamp"
+ />
+ <el-option
+ label="自动更新时间"
+ value="UpdateTimestamp"
+ />
+ </el-select>
+ </template>
+ </el-table-column>
+ <el-table-column label="关联字典">
+ <template slot-scope="scope">
+ <el-select v-model="data[scope.$index].dictName" filterable class="edit-input" clearable size="mini" placeholder="请选择">
+ <el-option v-for="item in dicts" :key="item.id" :label="item.remark === '' ? item.name : item.remark" :value="item.name" />
+ </el-select>
+ </template>
+ </el-table-column>
+ </el-table>
+ </el-form>
+ </el-card>
+ </el-col>
+ <el-col>
+ <el-card class="box-card" shadow="never">
+ <div slot="header" class="clearfix">
+ <span class="role-span">生成配置</span>
+ <el-button
+ :loading="configLoading"
+ icon="el-icon-check"
+ size="mini"
+ style="float: right; padding: 6px 9px"
+ type="primary"
+ @click="doSubmit"
+ >保存</el-button>
+ </div>
+ <el-form ref="form" :model="form" :rules="rules" size="small" label-width="78px">
+ <el-form-item label="作者名称" prop="author">
+ <el-input v-model="form.author" style="width: 40%" />
+ <span style="color: #C0C0C0;margin-left: 10px;">类上面的作者名称</span>
+ </el-form-item>
+ <el-form-item label="模块名称" prop="moduleName">
+ <el-input v-model="form.moduleName" style="width: 40%" />
+ <span style="color: #C0C0C0;margin-left: 10px;">模块的名称,请选择项目中已存在的模块</span>
+ </el-form-item>
+ <el-form-item label="至于包下" prop="pack">
+ <el-input v-model="form.pack" style="width: 40%" />
+ <span style="color: #C0C0C0;margin-left: 10px;">项目包的名称,生成的代码放到哪个包里面</span>
+ </el-form-item>
+ <el-form-item label="接口名称" prop="apiAlias">
+ <el-input v-model="form.apiAlias" style="width: 40%" />
+ <span style="color: #C0C0C0;margin-left: 10px;">接口的名称,用于控制器与接口文档中</span>
+ </el-form-item>
+ <el-form-item label="前端路径" prop="path">
+ <el-input v-model="form.path" style="width: 40%" />
+ <span style="color: #C0C0C0;margin-left: 10px;">输入views文件夹下的目录,不存在即创建</span>
+ </el-form-item>
+ <!-- <el-form-item label="接口目录">-->
+ <!-- <el-input v-model="form.apiPath" style="width: 40%" />-->
+ <!-- <span style="color: #C0C0C0;margin-left: 10px;">Api存放路径[src/api],为空则自动生成路径</span>-->
+ <!-- </el-form-item>-->
+ <el-form-item label="去表前缀" prop="prefix">
+ <el-input v-model="form.prefix" placeholder="默认不去除表前缀" style="width: 40%" />
+ <span style="color: #C0C0C0;margin-left: 10px;">默认不去除表前缀,可自定义</span>
+ </el-form-item>
+ <el-form-item label="是否覆盖" prop="cover">
+ <el-radio-group v-model="form.cover" size="mini" style="width: 40%">
+ <el-radio-button label="true">是</el-radio-button>
+ <el-radio-button label="false">否</el-radio-button>
+ </el-radio-group>
+ <span style="color: #C0C0C0;margin-left: 10px;">谨防误操作,请慎重选择</span>
+ </el-form-item>
+ </el-form>
+ </el-card>
+ </el-col>
+ </el-row>
+ </div>
+</template>
+
+<script>
+import crud from '@/mixins/crud'
+import { update, get } from '@/api/generator/genConfig'
+import { save, sync, generator } from '@/api/generator/generator'
+import { getDicts } from '@/api/system/dict'
+export default {
+ name: 'GeneratorConfig',
+ components: {},
+ mixins: [crud],
+ data() {
+ return {
+ activeName: 'first', tableName: '', tableHeight: 550, columnLoading: false, configLoading: false, dicts: [], syncLoading: false, genLoading: false,
+ form: { id: null, tableName: '', author: '', pack: '', path: '', moduleName: '', cover: 'false', apiPath: '', prefix: '', apiAlias: null },
+ rules: {
+ author: [
+ { required: true, message: '作者不能为空', trigger: 'blur' }
+ ],
+ pack: [
+ { required: true, message: '包路径不能为空', trigger: 'blur' }
+ ],
+ moduleName: [
+ { required: true, message: '包路径不能为空', trigger: 'blur' }
+ ],
+ path: [
+ { required: true, message: '前端路径不能为空', trigger: 'blur' }
+ ],
+ apiAlias: [
+ { required: true, message: '接口名称不能为空', trigger: 'blur' }
+ ],
+ cover: [
+ { required: true, message: '不能为空', trigger: 'blur' }
+ ]
+ }
+ }
+ },
+ created() {
+ this.tableHeight = document.documentElement.clientHeight - 385
+ this.tableName = this.$route.params.tableName
+ this.$nextTick(() => {
+ this.init()
+ get(this.tableName).then(data => {
+ this.form = data
+ this.form.cover = this.form.cover.toString()
+ })
+ getDicts().then(data => {
+ this.dicts = data
+ })
+ })
+ },
+ methods: {
+ beforeInit() {
+ this.url = 'api/generator/columns'
+ const tableName = this.tableName
+ this.params = { tableName }
+ return true
+ },
+ saveColumnConfig() {
+ this.columnLoading = true
+ save(this.data).then(res => {
+ this.notify('保存成功', 'success')
+ this.columnLoading = false
+ }).catch(err => {
+ this.columnLoading = false
+ console.log(err.response.data.message)
+ })
+ },
+ doSubmit() {
+ this.$refs['form'].validate((valid) => {
+ if (valid) {
+ this.configLoading = true
+ update(this.form).then(res => {
+ this.notify('保存成功', 'success')
+ this.form = res
+ this.form.cover = this.form.cover.toString()
+ this.configLoading = false
+ }).catch(err => {
+ this.configLoading = false
+ console.log(err.response.data.message)
+ })
+ }
+ })
+ },
+ sync() {
+ this.syncLoading = true
+ sync([this.tableName]).then(() => {
+ this.init()
+ this.notify('同步成功', 'success')
+ this.syncLoading = false
+ }).then(() => {
+ this.syncLoading = false
+ })
+ },
+ toGen() {
+ this.genLoading = true
+ save(this.data).then(res => {
+ this.notify('保存成功', 'success')
+ // 生成代码
+ generator(this.tableName, 0).then(data => {
+ this.genLoading = false
+ this.notify('生成成功', 'success')
+ }).catch(err => {
+ this.genLoading = false
+ console.log(err.response.data.message)
+ })
+ }).catch(err => {
+ this.genLoading = false
+ console.log(err.response.data.message)
+ })
+ }
+ }
+}
+</script>
+
+<style rel="stylesheet/scss" lang="scss">
+ .edit-input {
+ .el-input__inner {
+ border: 1px solid #e5e6e7;
+ }
+ }
+</style>
+
+<style scoped>
+ ::v-deep .input-with-select .el-input-group__prepend {
+ background-color: #fff;
+ }
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/generator/index.vue b/UI source code/dns_mapping_ui-master/src/views/generator/index.vue
new file mode 100644
index 0000000..6a07dc4
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/generator/index.vue
@@ -0,0 +1,114 @@
+<template>
+ <div class="app-container">
+ <!--工具栏-->
+ <div class="head-container">
+ <div v-if="crud.props.searchToggle">
+ <el-input v-model="query.name" clearable size="small" placeholder="请输入表名" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
+ <rrOperation />
+ </div>
+ <crudOperation>
+ <el-tooltip slot="right" class="item" effect="dark" content="数据库中表字段变动时使用该功能" placement="top-start">
+ <el-button
+ class="filter-item"
+ size="mini"
+ type="success"
+ icon="el-icon-refresh"
+ :loading="syncLoading"
+ :disabled="crud.selections.length === 0"
+ @click="sync"
+ >同步</el-button>
+ </el-tooltip>
+ </crudOperation>
+ </div>
+ <!--表格渲染-->
+ <el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
+ <el-table-column type="selection" width="55" />
+ <el-table-column :show-overflow-tooltip="true" prop="tableName" label="表名" />
+ <el-table-column :show-overflow-tooltip="true" prop="engine" label="数据库引擎" />
+ <el-table-column :show-overflow-tooltip="true" prop="coding" label="字符编码集" />
+ <el-table-column :show-overflow-tooltip="true" prop="remark" label="备注" />
+ <el-table-column prop="createTime" label="创建日期" />
+ <el-table-column label="操作" width="160px" align="center" fixed="right">
+ <template slot-scope="scope">
+ <el-button size="mini" style="margin-right: 2px" type="text">
+ <router-link :to="'/sys-tools/generator/preview/' + scope.row.tableName">
+ 预览
+ </router-link>
+ </el-button>
+ <el-button size="mini" style="margin-left: -1px;margin-right: 2px" type="text" @click="toDownload(scope.row.tableName)">下载</el-button>
+ <el-button size="mini" style="margin-left: -1px;margin-right: 2px" type="text">
+ <router-link :to="'/sys-tools/generator/config/' + scope.row.tableName">
+ 配置
+ </router-link>
+ </el-button>
+ <el-button type="text" style="margin-left: -1px" size="mini" @click="toGen(scope.row.tableName)">生成</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ <!--分页组件-->
+ <pagination />
+ </div>
+</template>
+
+<script>
+
+import { generator, sync } from '@/api/generator/generator'
+import { downloadFile } from '@/utils/index'
+import CRUD, { presenter, header } from '@crud/crud'
+import rrOperation from '@crud/RR.operation'
+import crudOperation from '@crud/CRUD.operation'
+import pagination from '@crud/Pagination'
+
+export default {
+ name: 'GeneratorIndex',
+ components: { pagination, crudOperation, rrOperation },
+ cruds() {
+ return CRUD({ url: 'api/generator/tables' })
+ },
+ mixins: [presenter(), header()],
+ data() {
+ return {
+ syncLoading: false
+ }
+ },
+ created() {
+ this.crud.optShow = { add: false, edit: false, del: false, download: false }
+ },
+ methods: {
+ toGen(tableName) {
+ // 生成代码
+ generator(tableName, 0).then(data => {
+ this.$notify({
+ title: '生成成功',
+ type: 'success',
+ duration: 2500
+ })
+ })
+ },
+ toDownload(tableName) {
+ // 打包下载
+ generator(tableName, 2).then(data => {
+ downloadFile(data, tableName, 'zip')
+ })
+ },
+ sync() {
+ const tables = []
+ this.crud.selections.forEach(val => {
+ tables.push(val.tableName)
+ })
+ this.syncLoading = true
+ sync(tables).then(() => {
+ this.crud.refresh()
+ this.crud.notify('同步成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
+ this.syncLoading = false
+ }).then(() => {
+ this.syncLoading = false
+ })
+ }
+ }
+}
+</script>
+
+<style scoped>
+
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/generator/preview.vue b/UI source code/dns_mapping_ui-master/src/views/generator/preview.vue
new file mode 100644
index 0000000..e95fc46
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/generator/preview.vue
@@ -0,0 +1,30 @@
+<template>
+ <el-tabs v-model="activeName" type="card">
+ <el-tab-pane v-for="item in data" :key="item.name" :lazy="true" :label="item.name" :name="item.name">
+ <Java :value="item.content" :height="height" />
+ </el-tab-pane>
+ </el-tabs>
+</template>
+
+<script>
+import Java from '@/components/JavaEdit/index'
+import { generator } from '@/api/generator/generator'
+export default {
+ name: 'Preview',
+ components: { Java },
+ data() {
+ return {
+ data: null, height: '', activeName: 'Entity'
+ }
+ },
+ created() {
+ this.height = document.documentElement.clientHeight - 180 + 'px'
+ const tableName = this.$route.params.tableName
+ generator(tableName, 1).then(data => {
+ this.data = data
+ }).catch(() => {
+ this.$router.go(-1)
+ })
+ }
+}
+</script>
diff --git a/UI source code/dns_mapping_ui-master/src/views/home.vue b/UI source code/dns_mapping_ui-master/src/views/home.vue
new file mode 100644
index 0000000..7093881
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/home.vue
@@ -0,0 +1,1074 @@
+<template>
+ <div class="dashboard-container">
+ <div class="form_list">
+ <div style="width: 100%; background-color: #fff">
+ <div class="form_title">
+ <div :class="tabshow == 1 ? 'active' : ''" @click="tab(1)">
+ <i class="el-icon-date" />服务数据
+ </div>
+ <div :class="tabshow == 2 ? 'active' : ''" @click="tab(2)">
+ <i class="el-icon-picture-outline" />网络数据
+ </div>
+ <div :class="tabshow == 3 ? 'active' : ''" @click="tab(3)">
+ <i class="el-icon-notebook-2" />主机数据
+ </div>
+ </div>
+ <div class="middle">
+ <!-- 父盒子 -->
+ <div class="father_box">
+ <div class="sea_box" @click="onclick()">
+ <div
+ v-for="(item, index) in keyword"
+ :key="index"
+ ref="Tag"
+ :class="index % 2 != 0 ? 'spanbox_value' : 'spanbox_key'"
+ >
+ <span v-if="Array.isArray(item)" class="tagspan">
+ <span v-for="(a,i) in item" :key="i">{{ a[a.length-1] }}</span>
+ </span>
+ <span v-else class="tagspan">{{ item }}</span>
+ <i
+ v-if="index % 2 != 0"
+ class="span_close"
+ @click="removeTag(index, item)"
+ />
+ </div>
+ <el-input
+ v-if="type === 'input'"
+ ref="inputTag"
+ v-model="currentval"
+ placeholder="请输入检索内容"
+ :style="inputStyle"
+ class="inputTag"
+ type="text"
+ @keydown.native="handleKeydown"
+ @focus="searchFoucs"
+ />
+ <el-select
+ v-else-if="type == 'select'"
+ ref="inputTag"
+ v-model="currentval"
+ placeholder="请输入检索内容"
+ :style="inputStyle"
+ class="inputTag"
+ type="text"
+ filterable
+ remote
+ default-first-option
+ @change="addTags"
+ @keyup.native="handleKeydown"
+ @focus="searchFoucs"
+ >
+ <el-option
+ v-for="item in options"
+ :key="item.value"
+ :label="item.label"
+ :value="item.value"
+ :disabled="item.disabled"
+ />
+ </el-select>
+
+ <el-cascader
+ v-else-if="type == 'more'"
+ ref="inputTag"
+ v-model="currentval"
+ :options="options"
+ :props="{ multiple: true, checkStrictly: true }"
+ clearable
+ :style="inputStyle"
+ class="inputTag"
+ type="text"
+ filterable
+ remote
+ default-first-option
+ @change="addTags"
+ @keyup.native="handleKeydown"
+ @focus="searchFoucs"
+ />
+
+ <el-select
+ v-else
+ ref="inputTag"
+ v-model="currentval"
+ placeholder="请输入检索内容"
+ :style="inputStyle"
+ class="inputTag"
+ type="text"
+ filterable
+ remote
+ default-first-option
+ @change="addTags"
+ @keyup.native="handleKeydown"
+ @focus="searchFoucs"
+ >
+ <el-option
+ v-for="item in options"
+ :key="item.value"
+ :label="item.label"
+ :value="item.value"
+ :disabled="item.disabled"
+ />
+ </el-select>
+ </div>
+ </div>
+ <!-- 搜索按钮 -->
+ <el-row>
+ <el-button
+ type="primary"
+ @click="search"
+ >搜&nbsp;&nbsp;索</el-button>
+ </el-row>
+ <!-- </el-form> -->
+ <div class="text">
+ <p class="massage">
+ 共<i v-if="loging" class="el-icon-loading" />
+ <span v-if="!loging">{{ list.resultTotal }}</span>条,含<i v-if="loging" class="el-icon-loading" />
+ <span v-if="!loging">
+ {{ handleIndependIpNum(list.independentIpNum) }}</span>
+
+ 个独立IP,<i v-if="loging" class="el-icon-loading" /><span
+ v-if="!loging"
+ class="second"
+ >用时{{ list.totalTime }}秒</span>
+ </p>
+ <div class="switch">
+ <el-switch v-model="value1" inactive-text="排除蜜罐" />
+ <el-switch v-model="value2" inactive-text="数据去重" />
+ <el-switch v-model="value3" inactive-text="排除CDN" />
+ <el-switch v-model="value4" inactive-text="最新数据" />
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="form_box">
+ <!-- 内容 -->
+ <div v-show="tabshow == 1">
+ <Sreviceleft v-if="leftShow" v-loading.lock="loging" />
+ <SreviceRight v-if="rightShow" v-loading.lock="loging" />
+ <div class="clear" />
+ <!-- 分页 -->
+ <div class="block">
+ <el-pagination
+ v-if="pageShow"
+ :page-sizes="[10, 20, 30, 40]"
+ :page-size="size"
+ layout="total, sizes, prev, pager, next, jumper"
+ :total="list.resultTotal"
+ @size-change="handleSizeChange"
+ @current-change="curPage"
+ />
+ </div>
+ <!-- <Foot /> -->
+ <!-- <pagination/> -->
+ </div>
+ <div v-show="tabshow == 2" />
+ <div v-show="tabshow == 3">3</div>
+ </div>
+ </div>
+ <!-- 返回顶部 -->
+ <div class="top">
+ <span
+ v-if="btnFlag"
+ class="backtop"
+ @click="toTop"
+ ><i
+ class="el-icon-top"
+ /></span>
+ </div>
+ </div>
+</template>
+
+<script>
+import Sreviceleft from '../components/Search/left.vue'
+import SreviceRight from '../components/Search/right.vue'
+import { mapActions, mapGetters } from 'vuex'
+import fackClickOutSide from '@/utils/fackClickOutSide'
+// import Search from "../components/SearchInput/SearchInput.vue";
+
+export default {
+ mixins:[fackClickOutSide],
+ name: 'InputTags',
+ components: {
+ Sreviceleft,
+ SreviceRight
+ // Search,
+ },
+ props: {
+ parentArr: {
+ type: Array,
+ default() {
+ return ['service', 'dns-doh']
+ }
+ },
+ limit: {
+ type: Number
+ }
+ },
+ data() {
+ return {
+ tabshow: 1,
+ value1: false,
+ value2: false,
+ value3: false,
+ value4: false,
+ pageShow: true,
+ rightShow: true,
+ leftShow: true,
+ btnFlag: false, // 返回顶部
+ // 搜索框
+ keyword: [],
+ obj: { service: 'dns-doh' },
+ obj1: [],
+ currentval: '',
+ inputLength: '',
+ // nav_list: true,
+ // // Loading: false,
+ // Loadshow: false,
+ options: [],
+ options1: [{
+ value: 'service',
+ label: 'service',
+ disabled: false
+ },
+ {
+ value: 'ip',
+ label: 'ip',
+ disabled: false
+ },
+ {
+ value: 'port',
+ label: 'port',
+ disabled: false
+ }],
+ // options3: [ {
+ // value: "dns-doh",
+ // label: "dns-doh",
+ // disabled: false
+ // },
+ // {
+ // value: "dns-do53",
+ // label: "dns-do53",
+ // disabled: false
+ // },],
+ options2: [
+ {
+ value: 'DNS',
+ label: 'DNS',
+ disabled: false,
+ children: [
+ {
+ value: 'dns-doh',
+ label: 'dns-doh',
+ disabled: false
+ // children: [
+ // {
+ // value: "proxy",
+ // label: "proxy",
+ // disabled: false,
+ // },
+ // {
+ // value: "server",
+ // label: "server",
+ // disabled: false,
+ // },
+ // ],
+
+ },
+ {
+ value: 'dns-do53',
+ label: 'dns-do53',
+ disabled: false
+ // children: [
+ // {
+ // value: "open-rdns",
+ // label: "open-rdns",
+ // disabled: false,
+ // },
+ // {
+ // value: "egress-dns",
+ // label: "egress-dns",
+ // disabled: false,
+ // },
+ // {
+ // value: "forwarder",
+ // label: "forwarder",
+ // disabled: false,
+ // },
+ // {
+ // value: "fwd/rdns",
+ // label: "fwd/rdns",
+ // disabled: false,
+ // },
+ // {
+ // value: "nonstandard",
+ // label: "nonstandard",
+ // disabled: false,
+ // },
+ // {
+ // value: "root",
+ // label: "root",
+ // disabled: false,
+ // },
+ // {
+ // value: "tld",
+ // label: "tld",
+ // disabled: false,
+ // },
+ // {
+ // value: "ns",
+ // label: "ns",
+ // disabled: false,
+ // },
+ // ],
+ }
+ ]
+ }
+ // {
+ // value: "email",
+ // label: "email",
+ // disabled: false,
+ // children: [
+ // {
+ // value: "email-1",
+ // label: "email-1",
+ // disabled: false,
+ // },
+ // {
+ // value: "email-2",
+ // label: "email-2",
+ // disabled: false,
+ // },
+ // ],
+ // },
+ ],
+ isFocus: false,
+ type: 'select',
+ newSer: [],
+ newip: [],
+ newPort: [],
+ find: { service: 'dns-doh' }
+ }
+ },
+ watch: {
+ keyword() {
+ this.$emit('on-change', this.keyword)
+ },
+ currentval(val) {
+ this.inputLength = this.$refs.inputTag.value.length * 12 + 50
+ },
+ parentArr() {
+ this.keyword = this.parentArr.length ? this.parentArr : []
+ }
+ },
+ created() {
+ window.addEventListener('scroll', this.scrollToTop)
+ // this.enterSearch();
+
+ // document.addEventListener('click', (e) => {
+ // var nav = document.getElementsByClassName('listhide')[0]
+ // if (nav.contains(e.target)) {
+ // this.nav_list = true
+ // } else {
+ // this.nav_list = false
+ // }
+ // })
+ },
+ destroyed() {
+ window.removeEventListener('scroll', () => {
+ this.scrollToTop()
+ })
+ },
+ computed: {
+ ...mapGetters({
+ list: 'searchlist/list',
+ size: 'searchlist/size',
+ left: 'searchlist/left',
+ // xylist: 'searchlist/xylist',
+ loging: 'searchlist/Loging'
+ }),
+ inputStyle() {
+ const style = {}
+ style.width = `${this.inputLength}px`
+ return style
+ },
+ finall() {
+ return this.keyword.join(',')
+ }
+ },
+
+ methods: {
+ // 删除
+ removeTag(index, item) {
+ // console.log(index, item);
+ this.keyword.splice(index, 1)
+ this.keyword.splice(index - 1, 1)
+ this.update()
+ // this.gs()
+ for (var i = 0; i < this.obj1.length; i++) {
+ if (item == this.obj1[i].service || item == this.obj1[i].ip || item == this.obj1[i].port) {
+ // console.log(this.obj1[i].service,i)
+ this.obj1.splice(i, 1)
+ i--
+ }
+ }
+ this.concat()
+ // this.contrast(this.newSer,item)
+ // this.contrast(this.newip,item)
+ // this.contrast(this.newPort,item)
+ // console.log(this.newSer,this.newip,this.newPort,"---del")
+ },
+ // 比较
+ // contrast(arr,item){
+ // if(arr){
+ // for(var i=0;i<arr.length;i++){
+ // // console.log(arr[0],item,90);
+ // if(item == arr[i]){
+ // return arr.splice(i,1)
+ // }
+ // }
+ // }
+
+ // },
+ edit() {
+ var str = []
+ this.keyword.forEach(item => {
+ if (Array.isArray(item)) {
+ item.forEach(a => {
+ str.push(a[a.length - 1])
+ })
+ } else {
+ str.push(item)
+ }
+ })
+ for (var i = 0; i < str.length; i++) {
+ if (str[i] == 'DNS') {
+ this.options2[this.options2.findIndex((item) => item.value === 'DNS')].disabled = true
+ for (var a = 0; a < this.options2.length; a++) {
+ if (this.options2[a].children) {
+ for (var j = 0; j < this.options2[a].children.length; j++) {
+ this.$set(this.options2[a].children[j], 'disabled', true)
+ if (this.options2[a].children[j].children) {
+ for (var b = 0; b < this.options2[a].children[j].children.length; b++) {
+ this.$set(this.options2[a].children[j].children[b], 'disabled', true)
+ }
+ }
+ }
+ }
+ }
+ } else if (str[i] == 'dns-doh') {
+ var text1 = this.options2[this.options2.findIndex((item) => item.value === 'DNS')].children
+ text1[text1.findIndex((item) => item.value === 'dns-doh')].disabled = true
+ for (var a = 0; a < text1.length; a++) {
+ if (text1[a].children) {
+ for (var b = 0; b < text1[a].children.length; b++) {
+ this.$set(text1[a].children[b], 'disabled', true)
+ }
+ }
+ }
+ } else if (str[i] == 'dns-do53') {
+ var text1 = this.options2[this.options2.findIndex((item) => item.value === 'DNS')].children
+ text1[text1.findIndex((item) => item.value === 'dns-do53')].disabled = true
+ for (var a = 0; a < text1.length; a++) {
+ if (text1[a].children) {
+ for (var b = 0; b < text1[a].children.length; b++) {
+ this.$set(text1[a].children[b], 'disabled', true)
+ }
+ }
+ }
+ }
+ }
+ },
+ update() {
+ this.options2.forEach(item => {
+ this.$set(item, 'disabled', false)
+ if (item.children) {
+ item.children.forEach(a => {
+ this.$set(a, 'disabled', false)
+ if (a.children) {
+ a.children.forEach(b => {
+ this.$set(b, 'disabled', false)
+ })
+ }
+ })
+ }
+ })
+ },
+ addTags(e) {
+ // console.log(this.currentval,e, "code输入");
+ // console.log(this.options1, this.options2, "下拉列表");
+ if (this.currentval != '') {
+ if (this.currentval == 'service') {
+ this.options = this.options2
+ this.type = 'more'
+ this.keyword.push(this.currentval)
+ this.currentval = ''
+ } else if (this.currentval == 'ip' || this.currentval == 'port') {
+ this.type = 'input'
+ this.keyword.push(this.currentval)
+ this.currentval = ''
+ } else {
+ this.options = this.options1
+ this.type = 'select'
+ this.keyword.push(this.currentval)
+ this.currentval = ''
+ }
+ // console.log(this.keyword);
+ this.edit()
+ this.concat()
+ }
+ },
+ // 拼接格式
+ concat() {
+ var str = []
+ this.keyword.forEach(item => {
+ if (Array.isArray(item)) {
+ item.forEach(a => {
+ str.push(a[a.length - 1])
+ })
+ } else {
+ str.push(item)
+ }
+ })
+ var ser = []
+ var ip = []
+ var port = []
+ for (var i = 1; i <= str.length / 2; i++) {
+ this.$set(this.obj, str[2 * i - 2], str[2 * i - 1])
+ if (str[2 * i - 2] == 'service') {
+ ser.push(str[2 * i - 1])
+ } else if (str[2 * i - 2] == 'ip') {
+ ip.push(str[2 * i - 1])
+ } else if (str[2 * i - 2] == 'port') {
+ port.push(str[2 * i - 1])
+ }
+ }
+ this.newSer = this.delRepeat(ser)
+ this.newip = this.delRepeat(ip)
+ this.newPort = this.delRepeat(port)
+ var serArr = this.newSer.join(' AND ')
+ var ipArr = this.newip.join(' AND ')
+ var portArr = this.newPort.join(' AND ')
+ this.$set(this.find, 'service', serArr)
+ this.$set(this.find, 'ip', ipArr)
+ this.$set(this.find, 'port', portArr)
+ // console.log(this.obj1,"1111");
+ console.log(this.find)
+ },
+ // 去重
+ delRepeat(arr) {
+ const newArr = []
+ for (let i = 0; i < arr.length; i++) {
+ let flag = true
+ for (let j = 0; j < newArr.length; j++) {
+ arr[i] === newArr[j] ? flag = false : flag
+ }
+ flag ? newArr.push(arr[i]) : newArr
+ }
+ return newArr
+ },
+ handleKeydown(e) {
+ console.log(e);
+ var keyCode = window.event ? e.keyCode : e.which
+ this.$nextTick(() => {
+ if (keyCode === 8) {
+ // console.log(this.$refs.inputTag.value,'----value');
+ // console.log(this.$refs.inputTag.query,'----query');
+ // console.log(this.currentval,'cur');
+
+ if (this.$refs.inputTag.query === '' || (this.currentval == '' && this.$refs.inputTag.query == undefined)) {
+ setTimeout(() => {
+ // console.log(888);
+ this.keyword.pop()
+ this.searchFoucs()
+ })
+ }
+ }
+ if (keyCode == 13) {
+ if (this.type == 'input') {
+ // console.log(this.$refs.inputTag,this.type,this.currentval,898);
+ // this.$refs.inputTag.visible = false;
+ setTimeout(() => {
+ this.addTags()
+ this.search()
+ })
+ } else if (this.type == 'select' && this.keyword.length % 2 === 0) {
+ setTimeout(() => {
+ this.addTags()
+ // this.keyword.pop();
+ this.search()
+ })
+ }
+ }
+ })
+ },
+ onclick() {
+ this.$nextTick(() => {
+ this.$refs.inputTag.focus();
+ this.searchFoucs()
+ // this.redit()
+ })
+ },
+ // 输入框键盘删除键删除tag
+ // deleteTags(state) {
+ // var keyCode = window.event ? e.keyCode : e.which;
+ // if (state) {
+ // setTimeout(() => {
+ // this.keyword.pop();
+ // this.searchFoucs();
+ // });
+ // }
+ // },
+ // changeArr(arr) {
+ // var item = {};
+ // // arr = arr.toString().split("");
+ // for (var i = 0; i < arr.length; i++) {
+ // var dt = arr[i];
+ // if (item[dt]) {
+ // item[dt]++;
+ // } else {
+ // item[dt] = 1;
+ // }
+ // }
+ // return item;
+ // },
+ searchFoucs() {
+ var str = []
+ this.keyword.forEach(item => {
+ if (Array.isArray(item)) {
+ item.forEach(a => {
+ str.push(a[a.length - 1])
+ })
+ } else {
+ str.push(item)
+ }
+ })
+ var len = str.length
+ var last = str[len - 1]
+ if (last == 'service') {
+ this.options = this.options2
+ this.type = 'more'
+ } else if (last == 'ip' || last == 'port') {
+ this.type = 'input'
+ } else {
+ this.options = this.options1
+ this.type = 'select'
+ }
+ this.update()
+ this.edit()
+ },
+ ...mapActions({
+ listActions: 'searchlist/listActions',
+ leftlistActions: 'searchlist/leftlistActions',
+ changePage: 'searchlist/pageActions',
+ changeSize: 'searchlist/sizeActions'
+ }),
+
+ // 搜索点击方法
+ search() {
+ this.addTags()
+ this.concat()
+ this.leftlistActions(this.find)
+ this.curPage(1)
+ this.pageShow = false
+ this.rightShow = false;
+ (this.leftShow = false),
+ this.$nextTick(() => {
+ this.pageShow = true
+ this.rightShow = true
+ this.leftShow = true
+ })
+ this.$store.state.searchlist.Loging = true
+ // console.log(this.obj,13);
+ },
+ // 回车搜索
+ // enterSearch() {
+ // document.onkeydown = (e) => {
+ // if (e.key === "Enter" && this.keyword.length % 2 ===0) {
+ // console.log("执行搜索", this.keyword.length % 2);
+ // this.search();
+ // }
+ // };
+ // },
+ // 返回顶部
+ toTop() {
+ window.scroll({
+ top: 0,
+ left: 0,
+ behavior: 'smooth'
+ })
+ },
+ scrollToTop() {
+ const scrollTop =
+ window.pageYOffset ||
+ document.documentElement.scrollTop ||
+ document.body.scrollTop
+ const h =
+ window.innerHeight ||
+ document.documentElement.clientHeight ||
+ document.body.clientHeight
+ if (scrollTop > h) {
+ this.btnFlag = true
+ } else {
+ this.btnFlag = false
+ }
+ },
+ // 分页
+ curPage(page) {
+ this.changePage(page)
+ this.listActions(this.find)
+ this.$store.state.searchlist.Loging = true
+ },
+ // 每页条数
+ handleSizeChange(val) {
+ this.changeSize(val)
+ // this.listActions(this.obj);
+ this.listActions(this.find)
+ },
+ // 选项卡切换
+ tab(val) {
+ this.tabshow = val
+ },
+ handleIndependIpNum(independentIpNum) {
+ if (!isNaN(independentIpNum)) {
+ return independentIpNum
+ } else {
+ // independentIpNum.forEach(a=>{
+ // res += a.key + ':' + a.value + ','
+ // })
+ let val = JSON.stringify(independentIpNum)
+ val = val.substring(1, val.length - 1)
+ return val.replaceAll('\"', '')
+ }
+ }
+ },
+ mounted() {
+ this.keyword = this.parentArr
+ this.listActions(this.find)
+ this.leftlistActions(this.find)
+ this.keyword = this.parentArr
+ this.options = this.options1
+ // this.gs()
+ this.concat()
+ }
+}
+</script>
+
+<style lang="scss" scoped>
+.dashboard-container {
+ // width: 91%;
+ // margin: 0px auto;
+ height: 100vh;
+ .form_list {
+ width: 100%;
+ padding-bottom: 20px;
+ background-color: #f9fafd;
+ .middle {
+ width: 91%;
+ margin: 0px auto;
+ background-color: white;
+ margin-bottom: 10px;
+ margin-top: 5px;
+ .el-button--primary {
+ background-color: #4608ad;
+ border-color: #4608ad;
+ }
+ }
+ .form_title {
+ width: 91%;
+ margin: 0px auto;
+ display: flex;
+ align-items: center;
+ height: 45px;
+ line-height: 45px;
+ text-align: center;
+ // margin-top: 10px;
+ // border: 1px solid transparent;
+ background-color: white;
+ div {
+ width: 180px;
+ padding-left: 5px;
+ background-color: #f9fafd;
+ border-radius: 10px 10px 0px 0px;
+ color: #000;
+ margin-right: 4px;
+ margin-top: 10px;
+ }
+ .active {
+ background-color: #4608ad;
+ color: white;
+ }
+ }
+ .form_box {
+ width: 91%;
+ margin: 0px auto;
+ }
+ .clear {
+ clear: both;
+ }
+ }
+ .search {
+ width: 100%;
+ height: 44px;
+ border: 1px solid #ccc;
+ border-radius: 0px 10px 10px 10px;
+ outline-style: none;
+ padding: 0;
+ }
+ .el-button--primary {
+ position: absolute;
+ right: 2px;
+ bottom: 4px;
+ height: 34px;
+ width: 132px;
+ font-size: 16px;
+ border-radius: 5px;
+ z-index: 1;
+ }
+ .text {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ .massage {
+ // background: palegreen;
+ width: 70%;
+ // float: left;
+ span {
+ color: #4608ad;
+ }
+ .second {
+ color: rgb(228, 107, 100);
+ }
+ }
+ .switch {
+ // background: pink;
+ // width: 30%;
+ // float: right;
+ margin: 16px 0px;
+ color: white !important;
+ div >>> .el-switch__label.is-active {
+ color: #ccc !important;
+ }
+ }
+ }
+ .backtop {
+ background-color: transparent;
+ border: 1px solid #4608ad;
+ border-radius: 50%;
+ text-align: center;
+ color: #4608ad;
+ }
+}
+@media only screen and (min-width: 768px) and (max-width: 1370px) {
+ .dashboard-container .text >>> .massage {
+ width: 60%;
+ }
+ .backtop {
+ position: fixed;
+ width: 40px;
+ height: 40px;
+ bottom: 40px;
+ right: 10px;
+ line-height: 40px;
+ }
+}
+@media only screen and (min-width: 1371px) and (max-width: 2400px) {
+ .dashboard-container .text >>> .massage {
+ width: 63%;
+ }
+ .backtop {
+ position: fixed;
+ width: 50px;
+ height: 50px;
+ bottom: 50px;
+ right: 20px;
+ line-height: 50px;
+ }
+}
+.el-select--small >>> .el-input--small .el-input__inner {
+ height: 44px !important;
+ border: 1px solid #ccc;
+ border-radius: 0px 10px 10px 10px;
+ outline-style: none;
+ padding: 0;
+ z-index: -3;
+}
+.block {
+ // display: flex;
+ // justify-content: flex-end;
+ float: right;
+ width: 76%;
+ height: 50px;
+ line-height: 50px;
+ background-color: #fff;
+}
+.block >>> .el-pagination {
+ height: 50px;
+ line-height: 50px;
+ padding: 10px 5px;
+ float: right;
+ .active {
+ color: #4608ad;
+ }
+}
+/* 外层div */
+.father_box {
+ .sea_box {
+ height: 40px;
+ line-height: 40px;
+ }
+ /* width: 300px; */
+ box-sizing: border-box;
+ background-color: white;
+ border: 1px solid #7b68ee;
+ border-radius: 0px 8px 8px 8px;
+ font-size: 12px;
+ text-align: left;
+ padding-left: 5px;
+ word-wrap: break-word;
+ overflow: hidden;
+}
+/* 标签 */
+.key,
+.value {
+ display: inline-block;
+ font-size: 14px;
+ margin: 3px 4px 3px 0;
+ background-color: rgb(229, 229, 229);
+ border: 1px solid #e8eaec;
+ border-radius: 3px;
+}
+// .spanbox {
+// display: inline-block;
+// font-size: 14px;
+// margin: 3px 4px 3px 0;
+// background-color: #e5e5e5;
+// border: 1px solid #e8eaec;
+// border-radius: 3px;
+// height: 30px;
+// line-height: 30px;
+// }
+.spanbox_value {
+ display: inline-block;
+ font-size: 14px;
+ margin: 6px 4px 3px 0;
+ background-color: #d9e4ff;
+ border: 1px solid #e8eaec;
+ border-radius: 3px;
+ height: 30px;
+ line-height: 30px;
+}
+.spanbox_key {
+ display: inline-block;
+ font-size: 14px;
+ margin: 6px 4px 3px 0;
+ background-color: #b5c8f8;
+ border: 1px solid #e8eaec;
+ border-radius: 3px;
+ height: 30px;
+ line-height: 30px;
+}
+.tagspan {
+ height: 24px;
+ line-height: 22px;
+ max-width: 100%;
+ position: relative;
+ display: inline-block;
+ padding-left: 8px;
+ padding-right: 8px;
+ color: #495060;
+ font-size: 14px;
+ cursor: pointer;
+ opacity: 1;
+ vertical-align: middle;
+ overflow: hidden;
+ transition: 0.25s linear;
+ color: #4608ad;
+ font-weight: 600;
+}
+.span_close {
+ padding: 0 4px 0 4px;
+ opacity: 1;
+ -webkit-filter: none;
+ filter: none;
+ color: #4608ad;
+ font-weight: 600;
+}
+.span_close:after {
+ content: "\00D7";
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ /* line-height: 27px; */
+ transition: 0.3s, color 0s;
+}
+/* input */
+.inputTag {
+ font-size: 16px;
+ border: none;
+ box-shadow: none;
+ outline: none;
+ background-color: transparent;
+ padding: 0;
+ width: auto;
+ min-width: 250px;
+ vertical-align: top;
+ height: 32px;
+ color: #495060;
+ line-height: 32px;
+}
+.el-select--small >>> .el-input--small .el-input__inner {
+ border: none;
+}
+.el-select--small >>> .el-input--small .el-input__suffix {
+ display: none;
+}
+.el-cascader >>>.el-input--small .el-input__inner{
+ border: none;
+}
+.el-cascader >>> .el-input--small .el-input__suffix {
+ display: none;
+}
+.el-cascader >>> .el-cascader__tags{
+ margin-top: 5px;
+ font-size: 13px;
+ padding: 0px;
+}
+.el-cascader >>> .el-cascader__search-input{
+ margin: 0px;
+}
+.el-input--small >>> .el-input__inner {
+ border: none;
+ margin-top: 5px;
+ font-size: 13px;
+ padding: 0px;
+}
+.nav_list {
+ position: relative;
+ width: 100%;
+
+ ul {
+ box-shadow: 5px 5px 16px rgba(0, 0, 0, 0.1);
+ width: 100%;
+ position: absolute;
+ padding: 0px;
+ margin: 0px;
+ list-style: none;
+ background-color: white;
+ top: 0;
+ left: 0;
+ z-index: 1;
+ li {
+ display: inline-block;
+ width: 100%;
+ height: 30px;
+ line-height: 30px;
+ padding-left: 12px;
+ }
+ }
+}
+// .el-cascader__dropdown{
+// position: absolute !important;
+// top: 174px !important;
+// left: 324px !important;
+// z-index: 1 !important;
+// }
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/login.vue b/UI source code/dns_mapping_ui-master/src/views/login.vue
new file mode 100644
index 0000000..0622e3c
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/login.vue
@@ -0,0 +1,215 @@
+<template>
+ <div class="login" :style="'background-image:url('+ Background +');'">
+ <el-form ref="loginForm" :model="loginForm" :rules="loginRules" label-position="left" label-width="0px" class="login-form">
+ <h3 class="title">
+ DiamondV 后台管理系统
+ </h3>
+ <el-form-item prop="username">
+ <el-input v-model="loginForm.username" type="text" auto-complete="off" placeholder="账号">
+ <svg-icon slot="prefix" icon-class="user" class="el-input__icon input-icon" />
+ </el-input>
+ </el-form-item>
+ <el-form-item prop="password">
+ <el-input v-model="loginForm.password" type="password" auto-complete="off" placeholder="密码" @keyup.enter.native="handleLogin">
+ <svg-icon slot="prefix" icon-class="password" class="el-input__icon input-icon" />
+ </el-input>
+ </el-form-item>
+ <!-- <el-form-item prop="code">
+ <el-input v-model="loginForm.code" auto-complete="off" placeholder="验证码" style="width: 63%" @keyup.enter.native="handleLogin">
+ <svg-icon slot="prefix" icon-class="validCode" class="el-input__icon input-icon" />
+ </el-input>
+ <div class="login-code">
+ <img :src="codeUrl" @click="getCode">
+ </div>
+ </el-form-item> -->
+ <!-- <el-checkbox v-model="loginForm.rememberMe" style="margin:0 0 25px 0;">
+ 记住我
+ </el-checkbox> -->
+ <el-form-item style="width:100%;">
+ <el-button :loading="loading" size="medium" type="primary" style="width:100%;" @click.native.prevent="handleLogin">
+ <span v-if="!loading">登 录</span>
+ <span v-else>登 录 中...</span>
+ </el-button>
+ </el-form-item>
+ </el-form>
+ <!-- 底部 -->
+ <!-- <div v-if="$store.state.settings.showFooter" id="el-login-footer">
+ <span v-html="$store.state.settings.footerTxt" />
+ <span> ⋅ </span>
+ <a href="https://beian.miit.gov.cn/#/Integrated/index" target="_blank">{{ $store.state.settings.caseNumber }}</a>
+ </div> -->
+ </div>
+</template>
+
+<script>
+import { encrypt } from '@/utils/rsaEncrypt'
+import Config from '@/settings'
+import { getCodeImg } from '@/api/login'
+import Cookies from 'js-cookie'
+import qs from 'qs'
+import Background from '@/assets/images/background.jpeg'
+export default {
+ name: 'Login',
+ data() {
+ return {
+ Background: Background,
+ codeUrl: '',
+ cookiePass: '',
+ loginForm: {
+ username: 'admin',
+ password: '123456',
+ rememberMe: false,
+ code: '',
+ uuid: ''
+ },
+ loginRules: {
+ username: [{ required: true, trigger: 'blur', message: '用户名不能为空' }],
+ password: [{ required: true, trigger: 'blur', message: '密码不能为空' }],
+ code: [{ required: true, trigger: 'change', message: '验证码不能为空' }]
+ },
+ loading: false,
+ redirect: undefined
+ }
+ },
+ watch: {
+ $route: {
+ handler: function(route) {
+ const data = route.query
+ if (data && data.redirect) {
+ this.redirect = data.redirect
+ delete data.redirect
+ if (JSON.stringify(data) !== '{}') {
+ this.redirect = this.redirect + '&' + qs.stringify(data, { indices: false })
+ }
+ }
+ },
+ immediate: true
+ }
+ },
+ created() {
+ // 获取验证码
+ this.getCode()
+ // 获取用户名密码等Cookie
+ this.getCookie()
+ // token 过期提示
+ this.point()
+ },
+ methods: {
+ getCode() {
+ getCodeImg().then(res => {
+ this.codeUrl = res.img
+ this.loginForm.uuid = res.uuid
+ })
+ },
+ getCookie() {
+ const username = Cookies.get('username')
+ let password = Cookies.get('password')
+ const rememberMe = Cookies.get('rememberMe')
+ // 保存cookie里面的加密后的密码
+ this.cookiePass = password === undefined ? '' : password
+ password = password === undefined ? this.loginForm.password : password
+ this.loginForm = {
+ username: username === undefined ? this.loginForm.username : username,
+ password: password,
+ rememberMe: rememberMe === undefined ? false : Boolean(rememberMe),
+ code: ''
+ }
+ },
+ handleLogin() {
+ this.$refs.loginForm.validate(valid => {
+ const user = {
+ username: this.loginForm.username,
+ password: this.loginForm.password,
+ rememberMe: this.loginForm.rememberMe,
+ code: this.loginForm.code,
+ uuid: this.loginForm.uuid
+ }
+ if (user.password !== this.cookiePass) {
+ user.password = encrypt(user.password)
+ }
+ if (valid) {
+ this.loading = true
+ if (user.rememberMe) {
+ Cookies.set('username', user.username, { expires: Config.passCookieExpires })
+ Cookies.set('password', user.password, { expires: Config.passCookieExpires })
+ Cookies.set('rememberMe', user.rememberMe, { expires: Config.passCookieExpires })
+ } else {
+ Cookies.remove('username')
+ Cookies.remove('password')
+ Cookies.remove('rememberMe')
+ }
+ this.$store.dispatch('Login', user).then(() => {
+ this.loading = false
+ this.$router.push({ path: this.redirect || '/' })
+ }).catch(() => {
+ this.loading = false
+ this.getCode()
+ })
+ } else {
+ console.log('error submit!!')
+ return false
+ }
+ })
+ },
+ point() {
+ const point = Cookies.get('point') !== undefined
+ if (point) {
+ this.$notify({
+ title: '提示',
+ message: '当前登录状态已过期,请重新登录!',
+ type: 'warning',
+ duration: 5000
+ })
+ Cookies.remove('point')
+ }
+ }
+ }
+}
+</script>
+
+<style rel="stylesheet/scss" lang="scss">
+ .login {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100%;
+ background-size: cover;
+ }
+ .title {
+ margin: 0 auto 30px auto;
+ text-align: center;
+ color: #707070;
+ }
+
+ .login-form {
+ border-radius: 6px;
+ background: #ffffff;
+ width: 385px;
+ padding: 25px 25px 5px 25px;
+ .el-input {
+ height: 38px;
+ input {
+ height: 38px;
+ }
+ }
+ .input-icon{
+ height: 39px;width: 14px;margin-left: 2px;
+ }
+ }
+ .login-tip {
+ font-size: 13px;
+ text-align: center;
+ color: #bfbfbf;
+ }
+ .login-code {
+ width: 33%;
+ display: inline-block;
+ height: 38px;
+ float: right;
+
+ img{
+ cursor: pointer;
+ vertical-align:middle
+ }
+ }
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/mnt/app/index.vue b/UI source code/dns_mapping_ui-master/src/views/mnt/app/index.vue
new file mode 100644
index 0000000..ddb8fb8
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/mnt/app/index.vue
@@ -0,0 +1,144 @@
+<template>
+ <div class="app-container">
+ <!--工具栏-->
+ <div class="head-container">
+ <div v-if="crud.props.searchToggle">
+ <!-- 搜索 -->
+ <el-input v-model="query.name" clearable placeholder="输入名称搜索" style="width: 200px" class="filter-item" @keyup.enter.native="crud.toQuery" />
+ <date-range-picker v-model="query.createTime" class="date-item" />
+ <rrOperation />
+ </div>
+ <crudOperation :permission="permission">
+ <el-button
+ slot="left"
+ v-permission="['admin','app:add']"
+ :disabled="!currentRow"
+ class="filter-item"
+ size="mini"
+ type="primary"
+ icon="el-icon-plus"
+ @click="copy"
+ >复制</el-button>
+ </crudOperation>
+ </div>
+ <!--表单组件-->
+ <el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="800px">
+ <el-form ref="form" :model="form" :rules="rules" size="small" label-width="100px">
+ <el-form-item label="应用名称" prop="name">
+ <el-input v-model="form.name" style="width: 670px" placeholder="部署后的文件或者目录名称,用于备份" />
+ </el-form-item>
+ <el-form-item label="应用端口" prop="port">
+ <el-input-number v-model.number="form.port" placeholder="例如:8080" />
+ </el-form-item>
+ <el-form-item label="上传目录" prop="uploadPath">
+ <el-input v-model="form.uploadPath" style="width: 670px" placeholder="例如: /opt/upload" />
+ </el-form-item>
+ <el-form-item label="部署目录" prop="deployPath">
+ <el-input v-model="form.deployPath" style="width: 670px" placeholder="例如: /opt/app" />
+ </el-form-item>
+ <el-form-item label="备份目录" prop="backupPath">
+ <el-input v-model="form.backupPath" style="width: 670px" placeholder="例如: /opt/backup" />
+ </el-form-item>
+ <el-form-item label="部署脚本" prop="deployScript">
+ <el-input v-model="form.deployScript" :rows="3" type="textarea" autosize style="width: 670px" placeholder="" />
+ </el-form-item>
+ <el-form-item label="启动脚本" prop="startScript">
+ <el-input v-model="form.startScript" :rows="3" type="textarea" autosize style="width: 670px" placeholder="" />
+ </el-form-item>
+ </el-form>
+ <div slot="footer" class="dialog-footer">
+ <el-button type="text" @click="crud.cancelCU">取消</el-button>
+ <el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
+ </div>
+ </el-dialog>
+ <!--表格渲染-->
+ <el-table ref="table" v-loading="crud.loading" :data="crud.data" highlight-current-row style="width: 100%" @selection-change="crud.selectionChangeHandler" @current-change="handleCurrentChange">
+ <el-table-column type="selection" width="55" />
+ <el-table-column prop="name" label="应用名称" />
+ <el-table-column prop="port" label="端口号" />
+ <el-table-column prop="uploadPath" label="上传目录" />
+ <el-table-column prop="deployPath" label="部署目录" />
+ <el-table-column prop="backupPath" label="备份目录" />
+ <el-table-column prop="createTime" label="创建日期" />
+ <el-table-column v-if="checkPer(['admin','app:edit','app:del'])" label="操作" width="150px" align="center">
+ <template slot-scope="scope">
+ <udOperation
+ :data="scope.row"
+ :permission="permission"
+ />
+ </template>
+ </el-table-column>
+ </el-table>
+ <!--分页组件-->
+ <pagination />
+ </div>
+</template>
+
+<script>
+import crudApp from '@/api/mnt/app'
+import CRUD, { presenter, header, form, crud } from '@crud/crud'
+import rrOperation from '@crud/RR.operation'
+import crudOperation from '@crud/CRUD.operation'
+import udOperation from '@crud/UD.operation'
+import pagination from '@crud/Pagination'
+import DateRangePicker from '@/components/DateRangePicker'
+
+const defaultForm = { id: null, name: null, port: 8080, uploadPath: '/opt/upload', deployPath: '/opt/app', backupPath: '/opt/backup', startScript: null, deployScript: null }
+export default {
+ name: 'App',
+ components: { pagination, crudOperation, rrOperation, udOperation, DateRangePicker },
+ cruds() {
+ return CRUD({ title: '应用', url: 'api/app', crudMethod: { ...crudApp }})
+ },
+ mixins: [presenter(), header(), form(defaultForm), crud()],
+ data() {
+ return {
+ currentRow: null,
+ permission: {
+ add: ['admin', 'app:add'],
+ edit: ['admin', 'app:edit'],
+ del: ['admin', 'app:del']
+ },
+ rules: {
+ name: [
+ { required: true, message: '请输入应用名称', trigger: 'blur' }
+ ],
+ port: [
+ { required: true, message: '请输入应用端口', trigger: 'blur', type: 'number' }
+ ],
+ uploadPath: [
+ { required: true, message: '请输入上传目录', trigger: 'blur' }
+ ],
+ deployPath: [
+ { required: true, message: '请输入部署目录', trigger: 'blur' }
+ ],
+ backupPath: [
+ { required: true, message: '请输入备份目录', trigger: 'blur' }
+ ],
+ startScript: [
+ { required: true, message: '请输入启动脚本', trigger: 'blur' }
+ ],
+ deployScript: [
+ { required: true, message: '请输入部署脚本', trigger: 'blur' }
+ ]
+ }
+ }
+ },
+ methods: {
+ copy() {
+ for (const key in this.currentRow) {
+ this.form[key] = this.currentRow[key]
+ }
+ this.form.id = null
+ this.form.createTime = null
+ this.crud.toAdd()
+ },
+ handleCurrentChange(row) {
+ this.currentRow = JSON.parse(JSON.stringify(row))
+ }
+ }
+}
+</script>
+
+<style scoped>
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/mnt/database/execute.vue b/UI source code/dns_mapping_ui-master/src/views/mnt/database/execute.vue
new file mode 100644
index 0000000..94622fc
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/mnt/database/execute.vue
@@ -0,0 +1,86 @@
+<template>
+ <el-dialog append-to-body :close-on-click-modal="false" :visible.sync="dialog" title="执行脚本" width="400px">
+ <el-form ref="form" :rules="rules" size="small">
+ <el-upload
+ :action="databaseUploadApi"
+ :data="databaseInfo"
+ :headers="headers"
+ :on-success="handleSuccess"
+ :on-error="handleError"
+ class="upload-demo"
+ drag
+ >
+ <i class="el-icon-upload" />
+ <div class="el-upload__text">
+ 将文件拖到此处,或
+ <em>点击上传</em>
+ </div>
+ <div slot="tip" class="el-upload__tip">上传后,系统会自动执行SQL脚本</div>
+ </el-upload>
+ </el-form>
+ <div slot="footer" class="dialog-footer">
+ <el-button type="primary" @click="cancel">关闭</el-button>
+ </div>
+ </el-dialog>
+</template>
+
+<script>
+import { mapGetters } from 'vuex'
+import { getToken } from '@/utils/auth'
+export default {
+ props: {
+ databaseInfo: {
+ type: Object,
+ default() {
+ return {}
+ }
+ }
+ },
+ data() {
+ return {
+ loading: false,
+ dialog: false,
+ headers: {
+ Authorization: getToken()
+ },
+ rules: {}
+ }
+ },
+ computed: {
+ ...mapGetters(['databaseUploadApi'])
+ },
+ mounted() {
+ },
+ methods: {
+ cancel() {
+ this.dialog = false
+ },
+ handleSuccess(response, file, fileList) {
+ if (response === 'success') {
+ this.$notify({
+ title: '执行成功',
+ type: 'success',
+ duration: 2500
+ })
+ } else {
+ this.$notify({
+ title: response,
+ type: 'error',
+ duration: 0
+ })
+ }
+ },
+ handleError(e, file, fileList) {
+ const msg = JSON.parse(e.message)
+ this.$notify({
+ title: msg.message,
+ type: 'error',
+ duration: 0
+ })
+ }
+ }
+}
+</script>
+
+<style scoped>
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/mnt/database/index.vue b/UI source code/dns_mapping_ui-master/src/views/mnt/database/index.vue
new file mode 100644
index 0000000..03d44a6
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/mnt/database/index.vue
@@ -0,0 +1,148 @@
+<template>
+ <div class="app-container">
+ <!--工具栏-->
+ <div class="head-container">
+ <div v-if="crud.props.searchToggle">
+ <!-- 搜索 -->
+ <el-input v-model="query.blurry" clearable placeholder="模糊搜索" style="width: 200px" class="filter-item" @keyup.enter.native="crud.toQuery" />
+ <date-range-picker v-model="query.createTime" class="date-item" />
+ <rrOperation />
+ </div>
+ <crudOperation :permission="permission">
+ <el-button
+ slot="right"
+ v-permission="['admin','database:add']"
+ :disabled="!selectIndex"
+ class="filter-item"
+ size="mini"
+ type="warning"
+ icon="el-icon-upload"
+ @click="execute"
+ >执行脚本
+ </el-button>
+ </crudOperation>
+ </div>
+ <!--表单组件-->
+ <eForm ref="execute" :database-info="currentRow" />
+ <el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="530px">
+ <el-form ref="form" :model="form" :rules="rules" size="small" label-width="100px">
+ <el-form-item label="连接名称" prop="name">
+ <el-input v-model="form.name" style="width: 370px" />
+ </el-form-item>
+ <el-form-item label="JDBC地址" prop="jdbcUrl">
+ <el-input v-model="form.jdbcUrl" style="width: 300px" />
+ <el-button :loading="loading" type="success" @click="testConnectDatabase">测试</el-button>
+ </el-form-item>
+ <el-form-item label="用户" prop="userName">
+ <el-input v-model="form.userName" style="width: 370px" />
+ </el-form-item>
+ <el-form-item label="密码" prop="pwd">
+ <el-input v-model="form.pwd" type="password" style="width: 370px" />
+ </el-form-item>
+ </el-form>
+ <div slot="footer" class="dialog-footer">
+ <el-button type="text" @click="crud.cancelCU">取消</el-button>
+ <el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
+ </div>
+ </el-dialog>
+ <!--表格渲染-->
+ <el-table ref="table" v-loading="crud.loading" :data="crud.data" highlight-current-row stripe style="width: 100%" @selection-change="handleCurrentChange">
+ <el-table-column type="selection" width="55" />
+ <el-table-column prop="name" width="130px" label="数据库名称" />
+ <el-table-column prop="jdbcUrl" label="连接地址" />
+ <el-table-column prop="userName" width="200px" label="用户名" />
+ <el-table-column prop="createTime" width="200px" label="创建日期" />
+ <el-table-column v-if="checkPer(['admin','database:edit','database:del'])" label="操作" width="150px" align="center">
+ <template slot-scope="scope">
+ <udOperation
+ :data="scope.row"
+ :permission="permission"
+ />
+ </template>
+ </el-table-column>
+ </el-table>
+ <!--分页组件-->
+ <pagination />
+ </div>
+</template>
+
+<script>
+import crudDatabase from '@/api/mnt/database'
+import { testDbConnect } from '@/api/mnt/connect'
+import eForm from './execute'
+import CRUD, { presenter, header, form, crud } from '@crud/crud'
+import rrOperation from '@crud/RR.operation'
+import crudOperation from '@crud/CRUD.operation'
+import udOperation from '@crud/UD.operation'
+import pagination from '@crud/Pagination'
+import DateRangePicker from '@/components/DateRangePicker'
+
+const defaultForm = { id: null, name: null, jdbcUrl: 'jdbc:mysql://', userName: null, pwd: null }
+export default {
+ name: 'DataBase',
+ components: { eForm, pagination, crudOperation, rrOperation, udOperation, DateRangePicker },
+ cruds() {
+ return CRUD({ title: '数据库', url: 'api/database', crudMethod: { ...crudDatabase }})
+ },
+ mixins: [presenter(), header(), form(defaultForm), crud()],
+ data() {
+ return {
+ currentRow: {},
+ selectIndex: '',
+ databaseInfo: '',
+ loading: false,
+ permission: {
+ add: ['admin', 'database:add'],
+ edit: ['admin', 'database:edit'],
+ del: ['admin', 'database:del']
+ },
+ rules: {
+ name: [
+ { required: true, message: '请输入数据库名称', trigger: 'blur' }
+ ],
+ jdbcUrl: [
+ { required: true, message: '请输入数据库连接地址', trigger: 'blur' }
+ ],
+ userName: [
+ { required: true, message: '请输入用户名', trigger: 'blur' }
+ ],
+ pwd: [
+ { required: true, message: '请输入数据库密码', trigger: 'blur' }
+ ]
+ }
+ }
+ },
+ methods: {
+ testConnectDatabase() {
+ this.$refs['form'].validate((valid) => {
+ if (valid) {
+ this.loading = true
+ testDbConnect(this.form).then((res) => {
+ this.loading = false
+ this.crud.notify(res ? '连接成功' : '连接失败', res ? 'success' : 'error')
+ }).catch(() => {
+ this.loading = false
+ })
+ }
+ })
+ },
+ execute() {
+ this.$refs.execute.dialog = true
+ },
+ handleCurrentChange(selection) {
+ this.crud.selections = selection
+ if (selection.length === 1) {
+ const row = selection[0]
+ this.selectIndex = row.id
+ this.currentRow = row
+ } else {
+ this.currentRow = {}
+ this.selectIndex = ''
+ }
+ }
+ }
+}
+</script>
+
+<style scoped>
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/mnt/deploy/deploy.vue b/UI source code/dns_mapping_ui-master/src/views/mnt/deploy/deploy.vue
new file mode 100644
index 0000000..4a411b4
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/mnt/deploy/deploy.vue
@@ -0,0 +1,190 @@
+<template>
+ <el-dialog append-to-body :close-on-click-modal="false" :visible.sync="dialog" title="应用部署" width="400px">
+ <el-form ref="form" :model="form" :rules="rules" size="small">
+ <el-upload
+ :action="deployUploadApi"
+ :data="deployInfo"
+ :headers="headers"
+ :on-success="handleSuccess"
+ :on-error="handleError"
+ class="upload-demo"
+ drag
+ >
+ <i class="el-icon-upload" />
+ <div class="el-upload__text">
+ 将文件拖到此处,或
+ <em>点击上传</em>
+ </div>
+ <div slot="tip" class="el-upload__tip">多个应用上传文件名称为all.zip,数据库更新脚本扩展名为.sql,上传成功后系统自动部署系统。</div>
+ </el-upload>
+ </el-form>
+ <div slot="footer" class="dialog-footer">
+ <el-button type="primary" @click="cancel">关闭</el-button>
+ </div>
+ </el-dialog>
+</template>
+
+<script>
+import { add, edit, getApps, getServers } from '@/api/mnt/deploy'
+import { mapGetters } from 'vuex'
+import { getToken } from '@/utils/auth'
+
+export default {
+ props: {},
+ data() {
+ return {
+ loading: false,
+ dialog: false,
+ apps: [],
+ servers: [],
+ headers: {
+ Authorization: getToken()
+ },
+ deployInfo: {},
+ form: {
+ id: '',
+ appId: '',
+ ip: '',
+ selectIp: []
+ },
+ rules: {}
+ }
+ },
+ computed: {
+ ...mapGetters(['deployUploadApi'])
+ },
+ created() {
+ this.initWebSocket()
+ },
+ mounted() {
+ this.initSelect()
+ },
+ methods: {
+ cancel() {
+ this.resetForm()
+ },
+ doSubmit() {
+ this.loading = true
+ if (this.isAdd) {
+ this.doAdd()
+ } else {
+ this.doEdit()
+ }
+ },
+ joinIp() {
+ this.form.ip = ''
+ this.form.selectIp.forEach(ip => {
+ if (this.form.ip !== '') {
+ this.form.ip += ','
+ }
+ this.form.ip += ip
+ })
+ },
+ doAdd() {
+ this.joinIp()
+ add(this.form)
+ .then(res => {
+ this.resetForm()
+ this.$notify({
+ title: '添加成功',
+ type: 'success',
+ duration: 2500
+ })
+ this.loading = false
+ this.$parent.init()
+ })
+ .catch(err => {
+ this.loading = false
+ console.log(err.response.data.message)
+ })
+ },
+ doEdit() {
+ this.joinIp()
+ edit(this.form)
+ .then(res => {
+ this.resetForm()
+ this.$notify({
+ title: '修改成功',
+ type: 'success',
+ duration: 2500
+ })
+ this.loading = false
+ this.$parent.init()
+ })
+ .catch(err => {
+ this.loading = false
+ console.log(err.response.data.message)
+ })
+ },
+ resetForm() {
+ this.dialog = false
+ this.$refs['form'].resetFields()
+ this.form = {
+ id: '',
+ appId: '',
+ ip: '',
+ selectIp: []
+ }
+ },
+ initSelect() {
+ getApps().then(res => {
+ this.apps = res.content
+ })
+ getServers().then(res => {
+ this.servers = res.content
+ })
+ },
+ handleSuccess(response, file, fileList) {
+ this.cancel()
+ },
+ // 监听上传失败
+ handleError(e, file, fileList) {
+ const msg = JSON.parse(e.message)
+ this.$notify({
+ title: msg.message,
+ type: 'error',
+ duration: 2500
+ })
+ },
+ initWebSocket() {
+ const wsUri = process.env.VUE_APP_WS_API + '/webSocket/deploy'
+ this.websock = new WebSocket(wsUri)
+ this.websock.onerror = this.webSocketOnError
+ this.websock.onmessage = this.webSocketOnMessage
+ },
+ webSocketOnError(e) {
+ this.$notify({
+ title: 'WebSocket连接发生错误',
+ type: 'error',
+ duration: 0
+ })
+ },
+ webSocketOnMessage(e) {
+ const data = JSON.parse(e.data)
+ if (data.msgType === 'INFO') {
+ this.$notify({
+ title: '',
+ message: data.msg,
+ type: 'success',
+ dangerouslyUseHTMLString: true,
+ duration: 5500
+ })
+ } else if (data.msgType === 'ERROR') {
+ this.$notify({
+ title: '',
+ message: data.msg,
+ dangerouslyUseHTMLString: true,
+ type: 'error',
+ duration: 0
+ })
+ }
+ },
+ webSocketSend(agentData) {
+ this.websock.send(agentData)
+ }
+ }
+}
+</script>
+
+<style scoped>
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/mnt/deploy/index.vue b/UI source code/dns_mapping_ui-master/src/views/mnt/deploy/index.vue
new file mode 100644
index 0000000..e2ab8a7
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/mnt/deploy/index.vue
@@ -0,0 +1,229 @@
+<template>
+ <div class="app-container">
+ <!--工具栏-->
+ <div class="head-container">
+ <div v-if="crud.props.searchToggle">
+ <!-- 搜索 -->
+ <el-input v-model="query.appName" clearable placeholder="输入应用名称查询" style="width: 200px" class="filter-item" @keyup.enter.native="crud.toQuery" />
+ <date-range-picker v-model="query.createTime" class="date-item" />
+ <rrOperation />
+ </div>
+ <crudOperation :permission="permission">
+ <template slot="right">
+ <el-button
+ v-permission="['admin','deploy:add']"
+ :disabled="!selectIndex"
+ class="filter-item"
+ size="mini"
+ type="primary"
+ icon="el-icon-upload"
+ @click="sysRestore"
+ >系统还原
+ </el-button>
+ <el-button
+ v-permission="['admin','deploy:add']"
+ :disabled="!selectIndex"
+ class="filter-item"
+ size="mini"
+ type="primary"
+ icon="el-icon-upload"
+ @click="serverStatus"
+ >状态查询
+ </el-button>
+ <el-button
+ v-permission="['admin','deploy:add']"
+ :disabled="!selectIndex"
+ class="filter-item"
+ size="mini"
+ type="success"
+ icon="el-icon-upload"
+ @click="startServer"
+ >启动
+ </el-button>
+ <el-button
+ v-permission="['admin','deploy:add']"
+ :disabled="!selectIndex"
+ class="filter-item"
+ size="mini"
+ type="danger"
+ icon="el-icon-upload"
+ @click="stopServer"
+ >停止
+ </el-button>
+ <el-button
+ v-permission="['admin','deploy:add']"
+ :disabled="!selectIndex"
+ class="filter-item"
+ size="mini"
+ type="warning"
+ icon="el-icon-upload"
+ @click="deploy"
+ >一键部署
+ </el-button>
+ </template>
+ </crudOperation>
+ </div>
+ <!--表单组件-->
+ <el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="500px">
+ <el-form ref="form" :model="form" :rules="rules" size="small" label-width="80px">
+ <el-form-item label="应用" prop="app.id">
+ <el-select v-model.number="form.app.id" placeholder="请选择" style="width: 370px">
+ <el-option v-for="item in apps" :key="item.id" :label="item.name" :value="item.id" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="服务器" prop="deploys">
+ <el-select v-model="form.deploys" multiple placeholder="请选择" style="width: 370px">
+ <el-option v-for="item in servers" :key="item.id" :label="item.name" :value="item.id" />
+ </el-select>
+ </el-form-item>
+ </el-form>
+ <div slot="footer" class="dialog-footer">
+ <el-button type="text" @click="crud.cancelCU">取消</el-button>
+ <el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
+ </div>
+ </el-dialog>
+ <!--统还原组件-->
+ <fForm ref="sysRestore" :key="times" :app-name="appName" />
+ <dForm ref="deploy" />
+ <!--表格渲染-->
+ <el-table ref="table" v-loading="crud.loading" :data="crud.data" highlight-current-row stripe style="width: 100%" @selection-change="handleCurrentChange">
+ <el-table-column type="selection" width="55" />
+ <el-table-column prop="app.name" label="应用名称" />
+ <el-table-column prop="servers" label="服务器列表" />
+ <el-table-column prop="createTime" label="部署日期" />
+ <el-table-column v-if="checkPer(['admin','deploy:edit','deploy:del'])" label="操作" width="150px" align="center">
+ <template slot-scope="scope">
+ <udOperation
+ :data="scope.row"
+ :permission="permission"
+ />
+ </template>
+ </el-table-column>
+ </el-table>
+ <!--分页组件-->
+ <pagination />
+ </div>
+</template>
+
+<script>
+import crudDeploy from '@/api/mnt/deploy'
+import dForm from './deploy'
+import fForm from './sysRestore'
+import CRUD, { presenter, header, form, crud } from '@crud/crud'
+import rrOperation from '@crud/RR.operation'
+import crudOperation from '@crud/CRUD.operation'
+import udOperation from '@crud/UD.operation'
+import pagination from '@crud/Pagination'
+import DateRangePicker from '@/components/DateRangePicker'
+
+const defaultForm = { id: null, app: { id: null }, deploys: [] }
+export default {
+ name: 'Deploy',
+ components: { dForm, fForm, pagination, crudOperation, rrOperation, udOperation, DateRangePicker },
+ cruds() {
+ return CRUD({ title: '部署', url: 'api/deploy', crudMethod: { ...crudDeploy }})
+ },
+ mixins: [presenter(), header(), form(defaultForm), crud()],
+ data() {
+ return {
+ currentRow: {}, selectIndex: '', appName: '', urlHistory: '',
+ times: 0, appId: '', deployId: '', apps: [], servers: [],
+ permission: {
+ add: ['admin', 'deploy:add'],
+ edit: ['admin', 'deploy:edit'],
+ del: ['admin', 'deploy:del']
+ },
+ rules: {
+ 'app.id': [
+ { required: true, message: '应用不能为空', trigger: 'blur', type: 'number' }
+ ],
+ deploys: [
+ { required: true, message: '服务器不能为空', trigger: 'blur' }
+ ]
+ }
+ }
+ },
+ methods: {
+ [CRUD.HOOK.beforeRefresh]() {
+ this.selectIndex = ''
+ return true
+ },
+ // 新增编辑前做的操作
+ [CRUD.HOOK.beforeToCU](crud, form) {
+ this.initSelect()
+ const deploys = []
+ form.deploys.forEach(function(deploy, index) {
+ deploys.push(deploy.id)
+ })
+ this.form.deploys = deploys
+ },
+ // 提交前
+ [CRUD.HOOK.beforeSubmit]() {
+ const deploys = []
+ this.form.deploys.forEach(function(data, index) {
+ const deploy = { id: data }
+ deploys.push(deploy)
+ })
+ this.form.deploys = deploys
+ return true
+ },
+ deploy() {
+ this.$refs.deploy.dialog = true
+ this.$refs.deploy.deployInfo = this.currentRow
+ },
+ sysRestore() {
+ this.$refs.sysRestore.dialog = true
+ },
+ handleCurrentChange(selection) {
+ this.crud.selections = selection
+ if (selection.length === 1) {
+ const row = selection[0]
+ this.selectIndex = row.id
+ this.currentRow = row
+ this.appName = row.app.name
+ this.times = this.times + 1
+ this.appId = row.appId
+ this.deployId = row.id
+ } else {
+ this.currentRow = {}
+ this.selectIndex = ''
+ }
+ },
+ startServer() {
+ crudDeploy.startServer(JSON.stringify(this.currentRow))
+ .then(res => {
+ })
+ .catch(err => {
+ console.log('error:' + err.response.data.message)
+ })
+ },
+ stopServer() {
+ crudDeploy.stopServer(JSON.stringify(this.currentRow))
+ .then(res => {
+ })
+ .catch(err => {
+ console.log('error:' + err.response.data.message)
+ })
+ },
+ serverStatus() {
+ crudDeploy.serverStatus(JSON.stringify(this.currentRow))
+ .then(res => {
+ })
+ .catch(err => {
+ console.log('error:' + err.response.data.message)
+ })
+ },
+ initSelect() {
+ crudDeploy.getApps().then(res => {
+ this.apps = res.content
+ })
+ crudDeploy.getServers().then(res => {
+ this.servers = res.content
+ })
+ }
+ }
+}
+</script>
+
+<style scoped>
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/mnt/deploy/sysRestore.vue b/UI source code/dns_mapping_ui-master/src/views/mnt/deploy/sysRestore.vue
new file mode 100644
index 0000000..e44aed6
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/mnt/deploy/sysRestore.vue
@@ -0,0 +1,108 @@
+<template>
+ <el-dialog append-to-body :close-on-click-modal="false" :visible.sync="dialog" title="系统还原" width="800px">
+ <!--工具栏-->
+ <div class="head-container">
+ <date-range-picker v-model="query.createTime" class="date-item" />
+ <el-button class="filter-item" size="mini" type="success" icon="el-icon-search" @click="toQuery">搜索</el-button>
+ </div>
+ <el-form size="small" label-width="80px">
+ <!--表格渲染-->
+ <el-table v-loading="loading" :data="data" style="width: 100%" @row-click="showRow">
+ <el-table-column width="30px">
+ <template slot-scope="scope">
+ <el-radio v-model="radio" :label="scope.$index" />
+ </template>
+ </el-table-column>
+ <el-table-column prop="appName" label="应用名称" />
+ <el-table-column prop="ip" label="部署IP" />
+ <el-table-column prop="deployDate" label="部署时间" />
+ <el-table-column prop="deployUser" label="部署人员" />
+ </el-table>
+ </el-form>
+ <div slot="footer" class="dialog-footer">
+ <el-button type="text" @click="cancel">取消</el-button>
+ <el-button v-permission="['admin','deploy:add']" :loading="submitLoading" type="primary" @click="doSubmit">确认</el-button>
+ </div>
+ <!--分页组件-->
+ <el-pagination
+ :total="total"
+ :current-page="page + 1"
+ style="margin-top: 8px"
+ layout="total, prev, pager, next, sizes"
+ @size-change="sizeChange"
+ @current-change="pageChange"
+ />
+ </el-dialog>
+</template>
+
+<script>
+import crud from '@/mixins/crud'
+import { reducte } from '@/api/mnt/deployHistory'
+import DateRangePicker from '@/components/DateRangePicker'
+export default {
+ components: { DateRangePicker },
+ mixins: [crud],
+ props: {
+ appName: {
+ type: String,
+ default: ''
+ }
+ },
+ data() {
+ return {
+ submitLoading: false,
+ dialog: false,
+ history: [],
+ radio: '',
+ appNames: '',
+ selectIndex: ''
+ }
+ },
+ created() {
+ this.$nextTick(() => {
+ this.init()
+ })
+ },
+ methods: {
+ beforeInit() {
+ this.url = 'api/deployHistory'
+ this.deployId = this.$parent.deployId
+ if (this.deployId === '') {
+ return false
+ }
+ this.sort = 'deployDate,desc'
+ this.params['deployId'] = this.deployId
+ return true
+ },
+ showRow(row) {
+ this.radio = this.data.indexOf(row)
+ this.selectIndex = row.id
+ },
+ cancel() {
+ this.dialog = false
+ this.submitLoading = false
+ },
+ doSubmit() {
+ if (this.selectIndex === '') {
+ this.$message.error('请选择要还原的备份')
+ } else {
+ this.submitLoading = true
+ reducte(JSON.stringify(this.data[this.radio]))
+ .then(res => {
+ this.dialog = false
+ this.submitLoading = false
+ this.appNames = ''
+ this.$parent.crud.toQuery()
+ })
+ .catch(err => {
+ this.submitLoading = false
+ console.log('error:' + err.response.data.message)
+ })
+ }
+ }
+ }
+}
+</script>
+
+<style scoped>
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/mnt/deployHistory/index.vue b/UI source code/dns_mapping_ui-master/src/views/mnt/deployHistory/index.vue
new file mode 100644
index 0000000..c4f9728
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/mnt/deployHistory/index.vue
@@ -0,0 +1,93 @@
+<template>
+ <div class="app-container">
+ <!--工具栏-->
+ <div class="head-container">
+ <div v-if="crud.props.searchToggle">
+ <!-- 搜索 -->
+ <el-input v-model="query.blurry" clearable placeholder="输入搜索内容" style="width: 200px" class="filter-item" @keyup.enter.native="crud.toQuery" />
+ <date-range-picker v-model="query.deployDate" class="date-item" />
+ <rrOperation />
+ </div>
+ <crudOperation :permission="permission" />
+ </div>
+ <!--表格渲染-->
+ <el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%" @selection-change="crud.selectionChangeHandler">
+ <el-table-column type="selection" width="55" />
+ <el-table-column prop="appName" label="应用名称" />
+ <el-table-column prop="ip" label="部署IP" />
+ <el-table-column prop="deployUser" label="部署人员" />
+ <el-table-column prop="deployDate" label="部署时间" />
+ <el-table-column v-if="checkPer(['admin','deployHistory:del'])" label="操作" width="100px" align="center">
+ <template slot-scope="scope">
+ <el-popover
+ :ref="scope.row.id"
+ v-permission="['admin','deployHistory:del']"
+ placement="top"
+ width="180"
+ >
+ <p>确定删除本条数据吗?</p>
+ <div style="text-align: right; margin: 0">
+ <el-button size="mini" type="text" @click="$refs[scope.row.id].doClose()">取消</el-button>
+ <el-button :loading="delLoading" type="primary" size="mini" @click="delMethod(scope.row.id)">确定</el-button>
+ </div>
+ <el-button slot="reference" type="danger" icon="el-icon-delete" size="mini" />
+ </el-popover>
+ </template>
+ </el-table-column>
+ </el-table>
+ <!--分页组件-->
+ <pagination />
+ </div>
+</template>
+
+<script>
+import { del } from '@/api/mnt/deployHistory'
+import CRUD, { presenter, header } from '@crud/crud'
+import rrOperation from '@crud/RR.operation'
+import crudOperation from '@crud/CRUD.operation'
+import pagination from '@crud/Pagination'
+import DateRangePicker from '@/components/DateRangePicker'
+
+export default {
+ name: 'DeployHistory',
+ components: { pagination, crudOperation, rrOperation, DateRangePicker },
+ cruds() {
+ return CRUD({ title: '部署历史', url: 'api/deployHistory', crudMethod: { del }})
+ },
+ mixins: [presenter(), header()],
+ data() {
+ return {
+ delLoading: false,
+ permission: {
+ del: ['admin', 'deployHistory:del']
+ }
+ }
+ },
+ created() {
+ this.crud.optShow = {
+ add: false,
+ edit: false,
+ del: true,
+ download: true
+ }
+ },
+ methods: {
+ delMethod(id) {
+ this.delLoading = true
+ del([id]).then(() => {
+ this.delLoading = false
+ this.$refs[id].doClose()
+ this.crud.dleChangePage(1)
+ this.crud.delSuccessNotify()
+ this.crud.toQuery()
+ }).catch(() => {
+ this.delLoading = false
+ this.$refs[id].doClose()
+ })
+ }
+ }
+}
+</script>
+
+<style scoped>
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/mnt/server/index.vue b/UI source code/dns_mapping_ui-master/src/views/mnt/server/index.vue
new file mode 100644
index 0000000..c26cc9c
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/mnt/server/index.vue
@@ -0,0 +1,136 @@
+<template>
+ <div class="app-container">
+ <!--工具栏-->
+ <div class="head-container">
+ <div v-if="crud.props.searchToggle">
+ <!-- 搜索 -->
+ <el-input v-model="query.id" clearable placeholder="输入名称或IP搜索" style="width: 200px" class="filter-item" @keyup.enter.native="crud.toQuery" />
+ <date-range-picker v-model="query.createTime" class="date-item" />
+ <rrOperation />
+ </div>
+ <crudOperation :permission="permission" />
+ </div>
+ <!--表单组件-->
+ <el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="470px">
+ <el-form ref="form" :model="form" :rules="rules" size="small" label-width="55px">
+ <el-form-item label="名称" prop="name">
+ <el-input v-model="form.name" style="width: 370px" />
+ </el-form-item>
+ <el-form-item label="IP" prop="ip">
+ <el-input v-model="form.ip" style="width: 370px" />
+ </el-form-item>
+ <el-form-item label="端口" prop="port">
+ <el-input-number v-model.number="form.port" controls-position="right" style="width: 370px;" />
+ </el-form-item>
+ <el-form-item label="账号" prop="account">
+ <el-input v-model="form.account" style="width: 370px" />
+ </el-form-item>
+ <el-form-item label="密码" prop="password">
+ <el-input v-model="form.password" type="password" style="width: 200px" />
+ <el-button :loading="loading" type="success" style="align: right;" @click="testConnectServer">测试连接</el-button>
+ </el-form-item>
+ </el-form>
+ <div slot="footer" class="dialog-footer">
+ <el-button type="text" @click="crud.cancelCU">取消</el-button>
+ <el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
+ </div>
+ </el-dialog>
+ <!--表格渲染-->
+ <el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%" @selection-change="crud.selectionChangeHandler">
+ <el-table-column type="selection" width="55" />
+ <el-table-column prop="name" label="名称" />
+ <el-table-column prop="ip" label="IP" />
+ <el-table-column prop="port" label="端口" />
+ <el-table-column prop="account" label="账号" />
+ <el-table-column prop="createTime" label="创建日期" />
+ <el-table-column v-if="checkPer(['admin','serverDeploy:edit','serverDeploy:del'])" label="操作" width="150px" align="center">
+ <template slot-scope="scope">
+ <udOperation
+ :data="scope.row"
+ :permission="permission"
+ />
+ </template>
+ </el-table-column>
+ </el-table>
+ <!--分页组件-->
+ <pagination />
+ </div>
+</template>
+
+<script>
+
+import crudServer from '@/api/mnt/serverDeploy'
+import { testServerConnect } from '@/api/mnt/connect'
+import { validateIP } from '@/utils/validate'
+import CRUD, { presenter, header, form, crud } from '@crud/crud'
+import rrOperation from '@crud/RR.operation'
+import crudOperation from '@crud/CRUD.operation'
+import udOperation from '@crud/UD.operation'
+import pagination from '@crud/Pagination'
+import DateRangePicker from '@/components/DateRangePicker'
+
+const defaultForm = { id: null, name: null, ip: null, port: 22, account: 'root', password: null }
+export default {
+ name: 'Server',
+ components: { pagination, crudOperation, rrOperation, udOperation, DateRangePicker },
+ cruds() {
+ return CRUD({ title: '服务器', url: 'api/serverDeploy', crudMethod: { ...crudServer }})
+ },
+ mixins: [presenter(), header(), form(defaultForm), crud()],
+ data() {
+ return {
+ accountList: [],
+ accountMap: {},
+ loading: false,
+ permission: {
+ add: ['admin', 'serverDeploy:add'],
+ edit: ['admin', 'serverDeploy:edit'],
+ del: ['admin', 'serverDeploy:del']
+ },
+ rules: {
+ name: [
+ { required: true, message: '请输入名称', trigger: 'blur' }
+ ],
+ ip: [
+ { required: true, message: '请输入IP', trigger: 'blur' },
+ { validator: validateIP, trigger: 'change' }
+ ],
+ port: [
+ { required: true, message: '请输入端口', trigger: 'blur', type: 'number' }
+ ],
+ account: [
+ { required: true, message: '请输入账号', trigger: 'blur' }
+ ],
+ password: [
+ { required: true, message: '请输入密码', trigger: 'blur' }
+ ]
+ }
+ }
+ },
+ methods: {
+ testConnectServer() {
+ this.$refs['form'].validate((valid) => {
+ if (valid) {
+ this.loading = true
+ testServerConnect(this.form).then((res) => {
+ this.loading = false
+ this.$notify({
+ title: res ? '连接成功' : '连接失败',
+ type: res ? 'success' : 'error',
+ duration: 2500
+ })
+ }).catch(() => {
+ this.loading = false
+ })
+ }
+ })
+ }
+ }
+}
+</script>
+
+<style rel="stylesheet/scss" lang="scss" scoped>
+ ::v-deep .el-input-number .el-input__inner {
+ text-align: left;
+ }
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/monitor/log/errorLog.vue b/UI source code/dns_mapping_ui-master/src/views/monitor/log/errorLog.vue
new file mode 100644
index 0000000..9f2004e
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/monitor/log/errorLog.vue
@@ -0,0 +1,135 @@
+<template>
+ <div class="app-container">
+ <div class="head-container">
+ <Search />
+ <crudOperation>
+ <el-button
+ slot="left"
+ class="filter-item"
+ type="danger"
+ icon="el-icon-delete"
+ size="mini"
+ :loading="crud.delAllLoading"
+ @click="confirmDelAll()"
+ >
+ 清空
+ </el-button>
+ </crudOperation>
+ </div>
+ <!--表格渲染-->
+ <el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
+ <el-table-column type="expand">
+ <template slot-scope="props">
+ <el-form label-position="left" inline class="demo-table-expand">
+ <el-form-item label="请求方法">
+ <span>{{ props.row.method }}</span>
+ </el-form-item>
+ <el-form-item label="请求参数">
+ <span>{{ props.row.params }}</span>
+ </el-form-item>
+ </el-form>
+ </template>
+ </el-table-column>
+ <el-table-column prop="username" label="用户名" />
+ <el-table-column prop="requestIp" label="IP" />
+ <el-table-column :show-overflow-tooltip="true" prop="address" label="IP来源" />
+ <el-table-column prop="description" label="描述" />
+ <el-table-column prop="browser" label="浏览器" />
+ <el-table-column prop="createTime" label="创建日期" />
+ <el-table-column label="异常详情" width="100px">
+ <template slot-scope="scope">
+ <el-button size="mini" type="text" @click="info(scope.row.id)">查看详情</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ <el-dialog :visible.sync="dialog" title="异常详情" append-to-body top="30px" width="85%">
+ <pre v-highlightjs="errorInfo"><code class="java" /></pre>
+ </el-dialog>
+ <!--分页组件-->
+ <pagination />
+ </div>
+</template>
+
+<script>
+import { getErrDetail, delAllError } from '@/api/monitor/log'
+import Search from './search'
+import CRUD, { presenter } from '@crud/crud'
+import crudOperation from '@crud/CRUD.operation'
+import pagination from '@crud/Pagination'
+
+export default {
+ name: 'ErrorLog',
+ components: { Search, crudOperation, pagination },
+ cruds() {
+ return CRUD({ title: '异常日志', url: 'api/logs/error' })
+ },
+ mixins: [presenter()],
+ data() {
+ return {
+ errorInfo: '', dialog: false
+ }
+ },
+ created() {
+ this.crud.optShow = {
+ add: false,
+ edit: false,
+ del: false,
+ download: true
+ }
+ },
+ methods: {
+ // 获取异常详情
+ info(id) {
+ this.dialog = true
+ getErrDetail(id).then(res => {
+ this.errorInfo = res.exception
+ })
+ },
+ confirmDelAll() {
+ this.$confirm(`确认清空所有异常日志吗?`, '提示', {
+ confirmButtonText: '确定',
+ cancelButtonText: '取消',
+ type: 'warning'
+ }).then(() => {
+ this.crud.delAllLoading = true
+ delAllError().then(res => {
+ this.crud.delAllLoading = false
+ this.crud.dleChangePage(1)
+ this.crud.delSuccessNotify()
+ this.crud.toQuery()
+ }).catch(err => {
+ this.crud.delAllLoading = false
+ console.log(err.response.data.message)
+ })
+ }).catch(() => {
+ })
+ }
+ }
+}
+</script>
+
+<style scoped>
+.demo-table-expand {
+ font-size: 0;
+}
+.demo-table-expand label {
+ width: 70px;
+ color: #99a9bf;
+}
+.demo-table-expand .el-form-item {
+ margin-right: 0;
+ margin-bottom: 0;
+ width: 100%;
+}
+.demo-table-expand .el-form-item__content {
+ font-size: 12px;
+}
+/deep/ .el-dialog__body {
+ padding: 0 20px 10px 20px !important;
+}
+.java.hljs {
+ color: #444;
+ background: #ffffff !important;
+ height: 630px !important;
+}
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/monitor/log/index.vue b/UI source code/dns_mapping_ui-master/src/views/monitor/log/index.vue
new file mode 100644
index 0000000..41a00dc
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/monitor/log/index.vue
@@ -0,0 +1,114 @@
+<template>
+ <div class="app-container">
+ <div class="head-container">
+ <Search />
+ <crudOperation>
+ <el-button
+ slot="left"
+ class="filter-item"
+ type="danger"
+ icon="el-icon-delete"
+ size="mini"
+ :loading="crud.delAllLoading"
+ @click="confirmDelAll()"
+ >
+ 清空
+ </el-button>
+ </crudOperation>
+ </div>
+ <!--表格渲染-->
+ <el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
+ <el-table-column type="expand">
+ <template slot-scope="props">
+ <el-form label-position="left" inline class="demo-table-expand">
+ <el-form-item label="请求方法">
+ <span>{{ props.row.method }}</span>
+ </el-form-item>
+ <el-form-item label="请求参数">
+ <span>{{ props.row.params }}</span>
+ </el-form-item>
+ </el-form>
+ </template>
+ </el-table-column>
+ <el-table-column prop="username" label="用户名" />
+ <el-table-column prop="requestIp" label="IP" />
+ <el-table-column :show-overflow-tooltip="true" prop="address" label="IP来源" />
+ <el-table-column prop="description" label="描述" />
+ <el-table-column prop="browser" label="浏览器" />
+ <el-table-column prop="time" label="请求耗时" align="center">
+ <template slot-scope="scope">
+ <el-tag v-if="scope.row.time <= 300">{{ scope.row.time }}ms</el-tag>
+ <el-tag v-else-if="scope.row.time <= 1000" type="warning">{{ scope.row.time }}ms</el-tag>
+ <el-tag v-else type="danger">{{ scope.row.time }}ms</el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column prop="createTime" label="创建日期" width="180px" />
+ </el-table>
+ <!--分页组件-->
+ <pagination />
+ </div>
+</template>
+
+<script>
+import Search from './search'
+import { delAllInfo } from '@/api/monitor/log'
+import CRUD, { presenter } from '@crud/crud'
+import crudOperation from '@crud/CRUD.operation'
+import pagination from '@crud/Pagination'
+
+export default {
+ name: 'Log',
+ components: { Search, crudOperation, pagination },
+ cruds() {
+ return CRUD({ title: '日志', url: 'api/logs' })
+ },
+ mixins: [presenter()],
+ created() {
+ this.crud.optShow = {
+ add: false,
+ edit: false,
+ del: false,
+ download: true
+ }
+ },
+ methods: {
+ confirmDelAll() {
+ this.$confirm(`确认清空所有操作日志吗?`, '提示', {
+ confirmButtonText: '确定',
+ cancelButtonText: '取消',
+ type: 'warning'
+ }).then(() => {
+ this.crud.delAllLoading = true
+ delAllInfo().then(res => {
+ this.crud.delAllLoading = false
+ this.crud.dleChangePage(1)
+ this.crud.delSuccessNotify()
+ this.crud.toQuery()
+ }).catch(err => {
+ this.crud.delAllLoading = false
+ console.log(err.response.data.message)
+ })
+ }).catch(() => {
+ })
+ }
+ }
+}
+</script>
+
+<style>
+.demo-table-expand {
+ font-size: 0;
+}
+.demo-table-expand label {
+ width: 70px;
+ color: #99a9bf;
+}
+.demo-table-expand .el-form-item {
+ margin-right: 0;
+ margin-bottom: 0;
+ width: 100%;
+}
+.demo-table-expand .el-form-item__content {
+ font-size: 12px;
+}
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/monitor/log/search.vue b/UI source code/dns_mapping_ui-master/src/views/monitor/log/search.vue
new file mode 100644
index 0000000..ffbcc06
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/monitor/log/search.vue
@@ -0,0 +1,24 @@
+<template>
+ <div v-if="crud.props.searchToggle">
+ <el-input
+ v-model="query.blurry"
+ clearable
+ size="small"
+ placeholder="请输入你要搜索的内容"
+ style="width: 200px;"
+ class="filter-item"
+ />
+ <date-range-picker v-model="query.createTime" class="date-item" />
+ <rrOperation />
+ </div>
+</template>
+
+<script>
+import { header } from '@crud/crud'
+import rrOperation from '@crud/RR.operation'
+import DateRangePicker from '@/components/DateRangePicker'
+export default {
+ components: { rrOperation, DateRangePicker },
+ mixins: [header()]
+}
+</script>
diff --git a/UI source code/dns_mapping_ui-master/src/views/monitor/online/index.vue b/UI source code/dns_mapping_ui-master/src/views/monitor/online/index.vue
new file mode 100644
index 0000000..b35224d
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/monitor/online/index.vue
@@ -0,0 +1,121 @@
+<template>
+ <div class="app-container">
+ <div class="head-container">
+ <div v-if="crud.props.searchToggle">
+ <el-input v-model="query.filter" clearable size="small" placeholder="全表模糊搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
+ <rrOperation />
+ </div>
+ <crudOperation>
+ <el-button
+ slot="left"
+ class="filter-item"
+ type="danger"
+ icon="el-icon-delete"
+ size="mini"
+ :loading="delLoading"
+ :disabled="crud.selections.length === 0"
+ @click="doDelete(crud.selections)"
+ >
+ 强退
+ </el-button>
+ </crudOperation>
+ </div>
+ <!--表格渲染-->
+ <el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
+ <el-table-column type="selection" width="55" />
+ <el-table-column prop="userName" label="用户名" />
+ <el-table-column prop="nickName" label="用户昵称" />
+ <el-table-column prop="dept" label="部门" />
+ <el-table-column prop="ip" label="登录IP" />
+ <el-table-column :show-overflow-tooltip="true" prop="address" label="登录地点" />
+ <el-table-column prop="browser" label="浏览器" />
+ <el-table-column prop="loginTime" label="登录时间" />
+ <el-table-column label="操作" width="70px" fixed="right">
+ <template slot-scope="scope">
+ <el-popover
+ :ref="scope.$index"
+ v-permission="['admin']"
+ placement="top"
+ width="180"
+ >
+ <p>确定强制退出该用户吗?</p>
+ <div style="text-align: right; margin: 0">
+ <el-button size="mini" type="text" @click="$refs[scope.$index].doClose()">取消</el-button>
+ <el-button :loading="delLoading" type="primary" size="mini" @click="delMethod(scope.row.key, scope.$index)">确定</el-button>
+ </div>
+ <el-button slot="reference" size="mini" type="text">强退</el-button>
+ </el-popover>
+ </template>
+ </el-table-column>
+ </el-table>
+ <!--分页组件-->
+ <pagination />
+ </div>
+</template>
+
+<script>
+import { del } from '@/api/monitor/online'
+import CRUD, { presenter, header, crud } from '@crud/crud'
+import rrOperation from '@crud/RR.operation'
+import crudOperation from '@crud/CRUD.operation'
+import pagination from '@crud/Pagination'
+
+export default {
+ name: 'OnlineUser',
+ components: { pagination, crudOperation, rrOperation },
+ cruds() {
+ return CRUD({ url: 'auth/online', title: '在线用户' })
+ },
+ mixins: [presenter(), header(), crud()],
+ data() {
+ return {
+ delLoading: false,
+ permission: {}
+ }
+ },
+ created() {
+ this.crud.msg.del = '强退成功!'
+ this.crud.optShow = {
+ add: false,
+ edit: false,
+ del: false,
+ download: true
+ }
+ },
+ methods: {
+ doDelete(datas) {
+ this.$confirm(`确认强退选中的${datas.length}个用户?`, '提示', {
+ confirmButtonText: '确定',
+ cancelButtonText: '取消',
+ type: 'warning'
+ }).then(() => {
+ this.delMethod(datas)
+ }).catch(() => {})
+ },
+ // 踢出用户
+ delMethod(key, index) {
+ const ids = []
+ if (key instanceof Array) {
+ key.forEach(val => {
+ ids.push(val.key)
+ })
+ } else ids.push(key)
+ this.delLoading = true
+ del(ids).then(() => {
+ this.delLoading = false
+ if (this.$refs[index]) {
+ this.$refs[index].doClose()
+ }
+ this.crud.dleChangePage(1)
+ this.crud.delSuccessNotify()
+ this.crud.toQuery()
+ }).catch(() => {
+ this.delLoading = false
+ if (this.$refs[index]) {
+ this.$refs[index].doClose()
+ }
+ })
+ }
+ }
+}
+</script>
diff --git a/UI source code/dns_mapping_ui-master/src/views/monitor/server/index.vue b/UI source code/dns_mapping_ui-master/src/views/monitor/server/index.vue
new file mode 100644
index 0000000..920861e
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/monitor/server/index.vue
@@ -0,0 +1,291 @@
+<template>
+ <div v-loading="!show" element-loading-text="数据加载中..." :style="!show ? 'height: 500px' : 'height: 100%'" class="app-container">
+ <div v-if="show">
+ <el-card class="box-card">
+ <div style="color: #666;font-size: 13px;">
+ <svg-icon icon-class="system" style="margin-right: 5px" />
+ <span>
+ 系统:{{ data.sys.os }}
+ </span>
+ <span>
+ IP:{{ data.sys.ip }}
+ </span>
+ <span>
+ 项目已不间断运行:{{ data.sys.day }}
+ </span>
+ <i class="el-icon-refresh" style="margin-left: 40px" @click="init" />
+ </div>
+ </el-card>
+ <el-card class="box-card">
+ <div slot="header" class="clearfix">
+ <span style="font-weight: bold;color: #666;font-size: 15px">状态</span>
+ </div>
+ <div>
+ <el-col :xs="24" :sm="24" :md="6" :lg="6" :xl="6" style="margin-bottom: 10px">
+ <div class="title">CPU使用率</div>
+ <el-tooltip placement="top-end">
+ <div slot="content" style="font-size: 12px;">
+ <div style="padding: 3px;">
+ {{ data.cpu.name }}
+ </div>
+ <div style="padding: 3px">
+ {{ data.cpu.package }}
+ </div>
+ <div style="padding: 3px">
+ {{ data.cpu.core }}
+ </div>
+ <div style="padding: 3px">
+ {{ data.cpu.logic }}
+ </div>
+ </div>
+ <div class="content">
+ <el-progress type="dashboard" :percentage="parseFloat(data.cpu.used)" />
+ </div>
+ </el-tooltip>
+ <div class="footer">{{ data.cpu.coreNumber }} 核心</div>
+ </el-col>
+ <el-col :xs="24" :sm="24" :md="6" :lg="6" :xl="6" style="margin-bottom: 10px">
+ <div class="title">内存使用率</div>
+ <el-tooltip placement="top-end">
+ <div slot="content" style="font-size: 12px;">
+ <div style="padding: 3px;">
+ 总量:{{ data.memory.total }}
+ </div>
+ <div style="padding: 3px">
+ 已使用:{{ data.memory.used }}
+ </div>
+ <div style="padding: 3px">
+ 空闲:{{ data.memory.available }}
+ </div>
+ </div>
+ <div class="content">
+ <el-progress type="dashboard" :percentage="parseFloat(data.memory.usageRate)" />
+ </div>
+ </el-tooltip>
+ <div class="footer">{{ data.memory.used }} / {{ data.memory.total }}</div>
+ </el-col>
+ <el-col :xs="24" :sm="24" :md="6" :lg="6" :xl="6" style="margin-bottom: 10px">
+ <div class="title">交换区使用率</div>
+ <el-tooltip placement="top-end">
+ <div slot="content" style="font-size: 12px;">
+ <div style="padding: 3px;">
+ 总量:{{ data.swap.total }}
+ </div>
+ <div style="padding: 3px">
+ 已使用:{{ data.swap.used }}
+ </div>
+ <div style="padding: 3px">
+ 空闲:{{ data.swap.available }}
+ </div>
+ </div>
+ <div class="content">
+ <el-progress type="dashboard" :percentage="parseFloat(data.swap.usageRate)" />
+ </div>
+ </el-tooltip>
+ <div class="footer">{{ data.swap.used }} / {{ data.swap.total }}</div>
+ </el-col>
+ <el-col :xs="24" :sm="24" :md="6" :lg="6" :xl="6" style="margin-bottom: 10px">
+ <div class="title">磁盘使用率</div>
+ <div class="content">
+ <el-tooltip placement="top-end">
+ <div slot="content" style="font-size: 12px;">
+ <div style="padding: 3px">
+ 总量:{{ data.disk.total }}
+ </div>
+ <div style="padding: 3px">
+ 空闲:{{ data.disk.available }}
+ </div>
+ </div>
+ <div class="content">
+ <el-progress type="dashboard" :percentage="parseFloat(data.disk.usageRate)" />
+ </div>
+ </el-tooltip>
+ </div>
+ <div class="footer">{{ data.disk.used }} / {{ data.disk.total }}</div>
+ </el-col>
+ </div>
+ </el-card>
+
+ <div>
+ <el-row :gutter="6">
+ <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" style="margin-bottom: 10px">
+ <el-card class="box-card">
+ <div slot="header" class="clearfix">
+ <span style="font-weight: bold;color: #666;font-size: 15px">CPU使用率监控</span>
+ </div>
+ <div>
+ <v-chart :options="cpuInfo" />
+ </div>
+ </el-card>
+ </el-col>
+ <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" style="margin-bottom: 10px">
+ <el-card class="box-card">
+ <div slot="header" class="clearfix">
+ <span style="font-weight: bold;color: #666;font-size: 15px">内存使用率监控</span>
+ </div>
+ <div>
+ <v-chart :options="memoryInfo" />
+ </div>
+ </el-card>
+ </el-col>
+ </el-row>
+ </div>
+ </div>
+ </div>
+</template>
+
+<script>
+import ECharts from 'vue-echarts'
+import 'echarts/lib/chart/line'
+import 'echarts/lib/component/polar'
+import { initData } from '@/api/data'
+export default {
+ name: 'ServerMonitor',
+ components: {
+ 'v-chart': ECharts
+ },
+ data() {
+ return {
+ show: false,
+ monitor: null,
+ url: 'api/monitor',
+ data: {},
+ cpuInfo: {
+ tooltip: {
+ trigger: 'axis'
+ },
+ xAxis: {
+ type: 'category',
+ boundaryGap: false,
+ data: []
+ },
+ yAxis: {
+ type: 'value',
+ min: 0,
+ max: 100,
+ interval: 20
+ },
+ series: [{
+ data: [],
+ type: 'line',
+ areaStyle: {
+ normal: {
+ color: 'rgb(32, 160, 255)' // 改变区域颜色
+ }
+ },
+ itemStyle: {
+ normal: {
+ color: '#6fbae1',
+ lineStyle: {
+ color: '#6fbae1' // 改变折线颜色
+ }
+ }
+ }
+ }]
+ },
+ memoryInfo: {
+ tooltip: {
+ trigger: 'axis'
+ },
+ xAxis: {
+ type: 'category',
+ boundaryGap: false,
+ data: []
+ },
+ yAxis: {
+ type: 'value',
+ min: 0,
+ max: 100,
+ interval: 20
+ },
+ series: [{
+ data: [],
+ type: 'line',
+ areaStyle: {
+ normal: {
+ color: 'rgb(32, 160, 255)' // 改变区域颜色
+ }
+ },
+ itemStyle: {
+ normal: {
+ color: '#6fbae1',
+ lineStyle: {
+ color: '#6fbae1' // 改变折线颜色
+ }
+ }
+ }
+ }]
+ }
+ }
+ },
+ created() {
+ this.init()
+ this.monitor = window.setInterval(() => {
+ setTimeout(() => {
+ this.init()
+ }, 2)
+ }, 3500)
+ },
+ destroyed() {
+ clearInterval(this.monitor)
+ },
+ methods: {
+ init() {
+ initData(this.url, {}).then(data => {
+ this.data = data
+ this.show = true
+ if (this.cpuInfo.xAxis.data.length >= 8) {
+ this.cpuInfo.xAxis.data.shift()
+ this.memoryInfo.xAxis.data.shift()
+ this.cpuInfo.series[0].data.shift()
+ this.memoryInfo.series[0].data.shift()
+ }
+ this.cpuInfo.xAxis.data.push(data.time)
+ this.memoryInfo.xAxis.data.push(data.time)
+ this.cpuInfo.series[0].data.push(parseFloat(data.cpu.used))
+ this.memoryInfo.series[0].data.push(parseFloat(data.memory.usageRate))
+ })
+ }
+ }
+}
+</script>
+
+<style rel="stylesheet/scss" lang="scss" scoped>
+ ::v-deep .box-card {
+ margin-bottom: 5px;
+ span {
+ margin-right: 28px;
+ }
+ .el-icon-refresh {
+ margin-right: 10px;
+ float: right;
+ cursor:pointer;
+ }
+ }
+ .cpu, .memory, .swap, .disk {
+ width: 20%;
+ float: left;
+ padding-bottom: 20px;
+ margin-right: 5%;
+ }
+ .title {
+ text-align: center;
+ font-size: 15px;
+ font-weight: 500;
+ color: #999;
+ margin-bottom: 16px;
+ }
+ .footer {
+ text-align: center;
+ font-size: 15px;
+ font-weight: 500;
+ color: #999;
+ margin-top: -5px;
+ margin-bottom: 10px;
+ }
+ .content {
+ text-align: center;
+ margin-top: 5px;
+ margin-bottom: 5px;
+ }
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/monitor/sql/index.vue b/UI source code/dns_mapping_ui-master/src/views/monitor/sql/index.vue
new file mode 100644
index 0000000..0ed9034
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/monitor/sql/index.vue
@@ -0,0 +1,16 @@
+<template>
+ <elFrame :src="sqlApi" />
+</template>
+<script>
+import { mapGetters } from 'vuex'
+import elFrame from '@/components/Iframe/index'
+export default {
+ name: 'Sql',
+ components: { elFrame },
+ computed: {
+ ...mapGetters([
+ 'sqlApi'
+ ])
+ }
+}
+</script>
diff --git a/UI source code/dns_mapping_ui-master/src/views/nested/menu1/menu1-1/index.vue b/UI source code/dns_mapping_ui-master/src/views/nested/menu1/menu1-1/index.vue
new file mode 100644
index 0000000..132fc3f
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/nested/menu1/menu1-1/index.vue
@@ -0,0 +1,36 @@
+<template>
+ <div class="app-container">
+ <el-alert :closable="false" title="三级菜单1" type="success" />
+ <el-form label-width="170px" style="margin-top: 20px">
+ <el-form-item label="三级菜单缓存功能测试区">
+ <el-input v-model="input" placeholder="请输入内容" style="width: 360px;" />
+ </el-form-item>
+ </el-form>
+ <div>
+ <blockquote class="my-blockquote"> 三级菜单缓存配置教程</blockquote>
+ <pre class="my-code">
+ 1、将前后端代码更新为最新版版本,或对照提交记录修改,点击查看-> <a href="https://gitee.com/elunez/eladmin/commit/43d1a63577f9d5347924355708429a2d210e29f7" target="_blank">提交(1)</a>、<a href="https://gitee.com/elunez/eladmin/commit/46393875148fcca5eaa327d4073f72edb3752f5c" target="_blank">提交(2)</a>、<a href="https://gitee.com/elunez/eladmin-web/commit/c93c99d8921abbb2c52afc806635f5ca08d6bda8" target="_blank">提交(3)</a>
+ 2、将 二级菜单 的 菜单类型 设置为 目录 级别,并且原有的 组件路径 需要清空
+ 3、将 三级菜单 的 菜单缓存 设置为 是,最后将 组件名称 填写正确
+ 4、具体设置可参考 菜单管理 的 多级菜单 配置进行进行相应的修改
+ </pre>
+ <blockquote class="my-blockquote">更多帮助</blockquote>
+ <pre class="my-code">QQ交流群:一群:891137268、二群:947578238、三群:659622532</pre>
+ </div>
+ </div>
+</template>
+<script>
+export default {
+ name: 'Test',
+ data() {
+ return {
+ input: ''
+ }
+ }
+}
+</script>
+<style scoped>
+ .my-code a{
+ color:#009688;
+ }
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/nested/menu1/menu1-2/index.vue b/UI source code/dns_mapping_ui-master/src/views/nested/menu1/menu1-2/index.vue
new file mode 100644
index 0000000..8508f4a
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/nested/menu1/menu1-2/index.vue
@@ -0,0 +1,5 @@
+<template>
+ <div style="padding:30px;">
+ <el-alert :closable="false" title="三级菜单2" type="success" />
+ </div>
+</template>
diff --git a/UI source code/dns_mapping_ui-master/src/views/nested/menu2/index.vue b/UI source code/dns_mapping_ui-master/src/views/nested/menu2/index.vue
new file mode 100644
index 0000000..b8283a2
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/nested/menu2/index.vue
@@ -0,0 +1,5 @@
+<template>
+ <div style="padding:30px;">
+ <el-alert :closable="false" title="二级菜单" />
+ </div>
+</template>
diff --git a/UI source code/dns_mapping_ui-master/src/views/system/dept/index.vue b/UI source code/dns_mapping_ui-master/src/views/system/dept/index.vue
new file mode 100644
index 0000000..d02b846
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/system/dept/index.vue
@@ -0,0 +1,254 @@
+<template>
+ <div class="app-container">
+ <!--工具栏-->
+ <div class="head-container">
+ <div v-if="crud.props.searchToggle">
+ <!-- 搜索 -->
+ <el-input v-model="query.name" clearable size="small" placeholder="输入部门名称搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
+ <date-range-picker v-model="query.createTime" class="date-item" />
+ <el-select v-model="query.enabled" clearable size="small" placeholder="状态" class="filter-item" style="width: 90px" @change="crud.toQuery">
+ <el-option v-for="item in enabledTypeOptions" :key="item.key" :label="item.display_name" :value="item.key" />
+ </el-select>
+ <rrOperation />
+ </div>
+ <crudOperation :permission="permission" />
+ </div>
+ <!--表单组件-->
+ <el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="500px">
+ <el-form ref="form" inline :model="form" :rules="rules" size="small" label-width="80px">
+ <el-form-item label="部门名称" prop="name">
+ <el-input v-model="form.name" style="width: 370px;" />
+ </el-form-item>
+ <el-form-item label="部门排序" prop="deptSort">
+ <el-input-number
+ v-model.number="form.deptSort"
+ :min="0"
+ :max="999"
+ controls-position="right"
+ style="width: 370px;"
+ />
+ </el-form-item>
+ <el-form-item label="顶级部门">
+ <el-radio-group v-model="form.isTop" style="width: 140px">
+ <el-radio label="1">是</el-radio>
+ <el-radio label="0">否</el-radio>
+ </el-radio-group>
+ </el-form-item>
+ <el-form-item label="状态" prop="enabled">
+ <el-radio v-for="item in dict.dept_status" :key="item.id" v-model="form.enabled" :label="item.value">{{ item.label }}</el-radio>
+ </el-form-item>
+ <el-form-item v-if="form.isTop === '0'" style="margin-bottom: 0;" label="上级部门" prop="pid">
+ <treeselect
+ v-model="form.pid"
+ :load-options="loadDepts"
+ :options="depts"
+ style="width: 370px;"
+ placeholder="选择上级类目"
+ />
+ </el-form-item>
+ </el-form>
+ <div slot="footer" class="dialog-footer">
+ <el-button type="text" @click="crud.cancelCU">取消</el-button>
+ <el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
+ </div>
+ </el-dialog>
+ <!--表格渲染-->
+ <el-table
+ ref="table"
+ v-loading="crud.loading"
+ lazy
+ :load="getDeptDatas"
+ :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
+ :data="crud.data"
+ row-key="id"
+ @select="crud.selectChange"
+ @select-all="crud.selectAllChange"
+ @selection-change="crud.selectionChangeHandler"
+ >
+ <el-table-column :selectable="checkboxT" type="selection" width="55" />
+ <el-table-column label="名称" prop="name" />
+ <el-table-column label="排序" prop="deptSort" />
+ <el-table-column label="状态" align="center" prop="enabled">
+ <template slot-scope="scope">
+ <el-switch
+ v-model="scope.row.enabled"
+ :disabled="scope.row.id === 1"
+ active-color="#409EFF"
+ inactive-color="#F56C6C"
+ @change="changeEnabled(scope.row, scope.row.enabled,)"
+ />
+ </template>
+ </el-table-column>
+ <el-table-column prop="createTime" label="创建日期" />
+ <el-table-column v-if="checkPer(['admin','dept:edit','dept:del'])" label="操作" width="130px" align="center" fixed="right">
+ <template slot-scope="scope">
+ <udOperation
+ :data="scope.row"
+ :permission="permission"
+ :disabled-dle="scope.row.id === 1"
+ msg="确定删除吗,如果存在下级节点则一并删除,此操作不能撤销!"
+ />
+ </template>
+ </el-table-column>
+ </el-table>
+ </div>
+</template>
+
+<script>
+import crudDept from '@/api/system/dept'
+import Treeselect from '@riophae/vue-treeselect'
+import '@riophae/vue-treeselect/dist/vue-treeselect.css'
+import { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect'
+import CRUD, { presenter, header, form, crud } from '@crud/crud'
+import rrOperation from '@crud/RR.operation'
+import crudOperation from '@crud/CRUD.operation'
+import udOperation from '@crud/UD.operation'
+import DateRangePicker from '@/components/DateRangePicker'
+
+const defaultForm = { id: null, name: null, isTop: '1', subCount: 0, pid: null, deptSort: 999, enabled: 'true' }
+export default {
+ name: 'Dept',
+ components: { Treeselect, crudOperation, rrOperation, udOperation, DateRangePicker },
+ cruds() {
+ return CRUD({ title: '部门', url: 'api/dept', crudMethod: { ...crudDept }})
+ },
+ mixins: [presenter(), header(), form(defaultForm), crud()],
+ // 设置数据字典
+ dicts: ['dept_status'],
+ data() {
+ return {
+ depts: [],
+ rules: {
+ name: [
+ { required: true, message: '请输入名称', trigger: 'blur' }
+ ],
+ deptSort: [
+ { required: true, message: '请输入序号', trigger: 'blur', type: 'number' }
+ ]
+ },
+ permission: {
+ add: ['admin', 'dept:add'],
+ edit: ['admin', 'dept:edit'],
+ del: ['admin', 'dept:del']
+ },
+ enabledTypeOptions: [
+ { key: 'true', display_name: '正常' },
+ { key: 'false', display_name: '禁用' }
+ ]
+ }
+ },
+ methods: {
+ getDeptDatas(tree, treeNode, resolve) {
+ const params = { pid: tree.id }
+ setTimeout(() => {
+ crudDept.getDepts(params).then(res => {
+ resolve(res.content)
+ })
+ }, 100)
+ },
+ // 新增与编辑前做的操作
+ [CRUD.HOOK.afterToCU](crud, form) {
+ if (form.pid !== null) {
+ form.isTop = '0'
+ } else if (form.id !== null) {
+ form.isTop = '1'
+ }
+ form.enabled = `${form.enabled}`
+ if (form.id != null) {
+ this.getSupDepts(form.id)
+ } else {
+ this.getDepts()
+ }
+ },
+ getSupDepts(id) {
+ crudDept.getDeptSuperior(id).then(res => {
+ const date = res.content
+ this.buildDepts(date)
+ this.depts = date
+ })
+ },
+ buildDepts(depts) {
+ depts.forEach(data => {
+ if (data.children) {
+ this.buildDepts(data.children)
+ }
+ if (data.hasChildren && !data.children) {
+ data.children = null
+ }
+ })
+ },
+ getDepts() {
+ crudDept.getDepts({ enabled: true }).then(res => {
+ this.depts = res.content.map(function(obj) {
+ if (obj.hasChildren) {
+ obj.children = null
+ }
+ return obj
+ })
+ })
+ },
+ // 获取弹窗内部门数据
+ loadDepts({ action, parentNode, callback }) {
+ if (action === LOAD_CHILDREN_OPTIONS) {
+ crudDept.getDepts({ enabled: true, pid: parentNode.id }).then(res => {
+ parentNode.children = res.content.map(function(obj) {
+ if (obj.hasChildren) {
+ obj.children = null
+ }
+ return obj
+ })
+ setTimeout(() => {
+ callback()
+ }, 100)
+ })
+ }
+ },
+ // 提交前的验证
+ [CRUD.HOOK.afterValidateCU]() {
+ if (this.form.pid !== null && this.form.pid === this.form.id) {
+ this.$message({
+ message: '上级部门不能为空',
+ type: 'warning'
+ })
+ return false
+ }
+ if (this.form.isTop === '1') {
+ this.form.pid = null
+ }
+ return true
+ },
+ // 改变状态
+ changeEnabled(data, val) {
+ this.$confirm('此操作将 "' + this.dict.label.dept_status[val] + '" ' + data.name + '部门, 是否继续?', '提示', {
+ confirmButtonText: '确定',
+ cancelButtonText: '取消',
+ type: 'warning'
+ }).then(() => {
+ crudDept.edit(data).then(res => {
+ this.crud.notify(this.dict.label.dept_status[val] + '成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
+ }).catch(err => {
+ data.enabled = !data.enabled
+ console.log(err.response.data.message)
+ })
+ }).catch(() => {
+ data.enabled = !data.enabled
+ })
+ },
+ checkboxT(row, rowIndex) {
+ return row.id !== 1
+ }
+ }
+}
+</script>
+
+<style rel="stylesheet/scss" lang="scss" scoped>
+ ::v-deep .vue-treeselect__control,::v-deep .vue-treeselect__placeholder,::v-deep .vue-treeselect__single-value {
+ height: 30px;
+ line-height: 30px;
+ }
+</style>
+<style rel="stylesheet/scss" lang="scss" scoped>
+ ::v-deep .el-input-number .el-input__inner {
+ text-align: left;
+ }
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/system/dict/dictDetail.vue b/UI source code/dns_mapping_ui-master/src/views/system/dict/dictDetail.vue
new file mode 100644
index 0000000..32756da
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/system/dict/dictDetail.vue
@@ -0,0 +1,115 @@
+<template>
+ <div>
+ <div v-if="query.dictName === ''">
+ <div class="my-code">点击字典查看详情</div>
+ </div>
+ <div v-else>
+ <!--工具栏-->
+ <div class="head-container">
+ <div v-if="crud.props.searchToggle">
+ <!-- 搜索 -->
+ <el-input v-model="query.label" clearable size="small" placeholder="输入字典标签查询" style="width: 200px;" class="filter-item" @keyup.enter.native="toQuery" />
+ <rrOperation />
+ </div>
+ </div>
+ <!--表单组件-->
+ <el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible="crud.status.cu > 0" :title="crud.status.title" width="500px">
+ <el-form ref="form" :model="form" :rules="rules" size="small" label-width="80px">
+ <el-form-item label="字典标签" prop="label">
+ <el-input v-model="form.label" style="width: 370px;" />
+ </el-form-item>
+ <el-form-item label="字典值" prop="value">
+ <el-input v-model="form.value" style="width: 370px;" />
+ </el-form-item>
+ <el-form-item label="排序" prop="dictSort">
+ <el-input-number v-model.number="form.dictSort" :min="0" :max="999" controls-position="right" style="width: 370px;" />
+ </el-form-item>
+ </el-form>
+ <div slot="footer" class="dialog-footer">
+ <el-button type="text" @click="crud.cancelCU">取消</el-button>
+ <el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
+ </div>
+ </el-dialog>
+ <!--表格渲染-->
+ <el-table ref="table" v-loading="crud.loading" :data="crud.data" highlight-current-row style="width: 100%;" @selection-change="crud.selectionChangeHandler">
+ <el-table-column label="所属字典">
+ {{ query.dictName }}
+ </el-table-column>
+ <el-table-column prop="label" label="字典标签" />
+ <el-table-column prop="value" label="字典值" />
+ <el-table-column prop="dictSort" label="排序" />
+ <el-table-column v-if="checkPer(['admin','dict:edit','dict:del'])" label="操作" width="130px" align="center" fixed="right">
+ <template slot-scope="scope">
+ <udOperation
+ :data="scope.row"
+ :permission="permission"
+ />
+ </template>
+ </el-table-column>
+ </el-table>
+ <!--分页组件-->
+ <pagination />
+ </div>
+ </div>
+</template>
+
+<script>
+import crudDictDetail from '@/api/system/dictDetail'
+import CRUD, { presenter, header, form } from '@crud/crud'
+import pagination from '@crud/Pagination'
+import rrOperation from '@crud/RR.operation'
+import udOperation from '@crud/UD.operation'
+
+const defaultForm = { id: null, label: null, value: null, dictSort: 999 }
+
+export default {
+ components: { pagination, rrOperation, udOperation },
+ cruds() {
+ return [
+ CRUD({ title: '字典详情', url: 'api/dictDetail', query: { dictName: '' }, sort: ['dictSort,asc', 'id,desc'],
+ crudMethod: { ...crudDictDetail },
+ optShow: {
+ add: true,
+ edit: true,
+ del: true,
+ reset: false
+ },
+ queryOnPresenterCreated: false
+ })
+ ]
+ },
+ mixins: [
+ presenter(),
+ header(),
+ form(function() {
+ return Object.assign({ dict: { id: this.dictId }}, defaultForm)
+ })],
+ data() {
+ return {
+ dictId: null,
+ rules: {
+ label: [
+ { required: true, message: '请输入字典标签', trigger: 'blur' }
+ ],
+ value: [
+ { required: true, message: '请输入字典值', trigger: 'blur' }
+ ],
+ dictSort: [
+ { required: true, message: '请输入序号', trigger: 'blur', type: 'number' }
+ ]
+ },
+ permission: {
+ add: ['admin', 'dict:add'],
+ edit: ['admin', 'dict:edit'],
+ del: ['admin', 'dict:del']
+ }
+ }
+ }
+}
+</script>
+
+<style rel="stylesheet/scss" lang="scss" scoped>
+ ::v-deep .el-input-number .el-input__inner {
+ text-align: left;
+ }
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/system/dict/index.vue b/UI source code/dns_mapping_ui-master/src/views/system/dict/index.vue
new file mode 100644
index 0000000..f102023
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/system/dict/index.vue
@@ -0,0 +1,135 @@
+<template>
+ <div class="app-container">
+ <!--表单组件-->
+ <el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible="crud.status.cu > 0" :title="crud.status.title" width="500px">
+ <el-form ref="form" :model="form" :rules="rules" size="small" label-width="80px">
+ <el-form-item label="字典名称" prop="name">
+ <el-input v-model="form.name" style="width: 370px;" />
+ </el-form-item>
+ <el-form-item label="描述">
+ <el-input v-model="form.description" style="width: 370px;" />
+ </el-form-item>
+ </el-form>
+ <div slot="footer" class="dialog-footer">
+ <el-button type="text" @click="crud.cancelCU">取消</el-button>
+ <el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
+ </div>
+ </el-dialog>
+ <!-- 字典列表 -->
+ <el-row :gutter="10">
+ <el-col :xs="24" :sm="24" :md="10" :lg="11" :xl="11" style="margin-bottom: 10px">
+ <el-card class="box-card">
+ <!--工具栏-->
+ <div class="head-container">
+ <div v-if="crud.props.searchToggle">
+ <!-- 搜索 -->
+ <el-input v-model="query.blurry" clearable size="small" placeholder="输入名称或者描述搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
+ <rrOperation />
+ </div>
+ <crudOperation :permission="permission" />
+ </div>
+ <!--表格渲染-->
+ <el-table ref="table" v-loading="crud.loading" :data="crud.data" highlight-current-row style="width: 100%;" @selection-change="crud.selectionChangeHandler" @current-change="handleCurrentChange">
+ <el-table-column type="selection" width="55" />
+ <el-table-column :show-overflow-tooltip="true" prop="name" label="名称" />
+ <el-table-column :show-overflow-tooltip="true" prop="description" label="描述" />
+ <el-table-column v-if="checkPer(['admin','dict:edit','dict:del'])" label="操作" width="130px" align="center" fixed="right">
+ <template slot-scope="scope">
+ <udOperation
+ :data="scope.row"
+ :permission="permission"
+ />
+ </template>
+ </el-table-column>
+ </el-table>
+ <!--分页组件-->
+ <pagination />
+ </el-card>
+ </el-col>
+ <!-- 字典详情列表 -->
+ <el-col :xs="24" :sm="24" :md="14" :lg="13" :xl="13">
+ <el-card class="box-card">
+ <div slot="header" class="clearfix">
+ <span>字典详情</span>
+ <el-button
+ v-if="checkPer(['admin','dict:add']) && this.$refs.dictDetail && this.$refs.dictDetail.query.dictName"
+ class="filter-item"
+ size="mini"
+ style="float: right;padding: 4px 10px"
+ type="primary"
+ icon="el-icon-plus"
+ @click="$refs.dictDetail && $refs.dictDetail.crud.toAdd()"
+ >新增</el-button>
+ </div>
+ <dictDetail ref="dictDetail" :permission="permission" />
+ </el-card>
+ </el-col>
+ </el-row>
+ </div>
+</template>
+
+<script>
+import dictDetail from './dictDetail'
+import crudDict from '@/api/system/dict'
+import CRUD, { presenter, header, form } from '@crud/crud'
+import crudOperation from '@crud/CRUD.operation'
+import pagination from '@crud/Pagination'
+import rrOperation from '@crud/RR.operation'
+import udOperation from '@crud/UD.operation'
+
+const defaultForm = { id: null, name: null, description: null, dictDetails: [] }
+
+export default {
+ name: 'Dict',
+ components: { crudOperation, pagination, rrOperation, udOperation, dictDetail },
+ cruds() {
+ return [
+ CRUD({ title: '字典', url: 'api/dict', crudMethod: { ...crudDict }})
+ ]
+ },
+ mixins: [presenter(), header(), form(defaultForm)],
+ data() {
+ return {
+ queryTypeOptions: [
+ { key: 'name', display_name: '字典名称' },
+ { key: 'description', display_name: '描述' }
+ ],
+ rules: {
+ name: [
+ { required: true, message: '请输入名称', trigger: 'blur' }
+ ]
+ },
+ permission: {
+ add: ['admin', 'dict:add'],
+ edit: ['admin', 'dict:edit'],
+ del: ['admin', 'dict:del']
+ }
+ }
+ },
+ methods: {
+ // 获取数据前设置好接口地址
+ [CRUD.HOOK.beforeRefresh]() {
+ if (this.$refs.dictDetail) {
+ this.$refs.dictDetail.query.dictName = ''
+ }
+ return true
+ },
+ // 选中字典后,设置字典详情数据
+ handleCurrentChange(val) {
+ if (val) {
+ this.$refs.dictDetail.query.dictName = val.name
+ this.$refs.dictDetail.dictId = val.id
+ this.$refs.dictDetail.crud.toQuery()
+ }
+ },
+ // 编辑前将字典明细临时清空,避免日志入库数据过长
+ [CRUD.HOOK.beforeToEdit](crud, form) {
+ // 将角色的菜单清空,避免日志入库数据过长
+ form.dictDetails = null
+ }
+ }
+}
+</script>
+
+<style scoped>
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/system/job/index.vue b/UI source code/dns_mapping_ui-master/src/views/system/job/index.vue
new file mode 100644
index 0000000..e17ea75
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/system/job/index.vue
@@ -0,0 +1,110 @@
+<template>
+ <div class="app-container">
+ <!--工具栏-->
+ <div class="head-container">
+ <eHeader :dict="dict" :permission="permission" />
+ <crudOperation :permission="permission" />
+ </div>
+ <!--表格渲染-->
+ <el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
+ <el-table-column type="selection" width="55" />
+ <el-table-column prop="name" label="名称" />
+ <el-table-column prop="jobSort" label="排序">
+ <template slot-scope="scope">
+ {{ scope.row.jobSort }}
+ </template>
+ </el-table-column>
+ <el-table-column prop="status" label="状态" align="center">
+ <template slot-scope="scope">
+ <el-switch
+ v-model="scope.row.enabled"
+ active-color="#409EFF"
+ inactive-color="#F56C6C"
+ @change="changeEnabled(scope.row, scope.row.enabled)"
+ />
+ </template>
+ </el-table-column>
+ <el-table-column prop="createTime" label="创建日期" />
+ <!-- 编辑与删除 -->
+ <el-table-column
+ v-if="checkPer(['admin','job:edit','job:del'])"
+ label="操作"
+ width="130px"
+ align="center"
+ fixed="right"
+ >
+ <template slot-scope="scope">
+ <udOperation
+ :data="scope.row"
+ :permission="permission"
+ />
+ </template>
+ </el-table-column>
+ </el-table>
+ <!--分页组件-->
+ <pagination />
+ <!--表单渲染-->
+ <eForm :job-status="dict.job_status" />
+ </div>
+</template>
+
+<script>
+import crudJob from '@/api/system/job'
+import eHeader from './module/header'
+import eForm from './module/form'
+import CRUD, { presenter } from '@crud/crud'
+import crudOperation from '@crud/CRUD.operation'
+import pagination from '@crud/Pagination'
+import udOperation from '@crud/UD.operation'
+export default {
+ name: 'Job',
+ components: { eHeader, eForm, crudOperation, pagination, udOperation },
+ cruds() {
+ return CRUD({
+ title: '岗位',
+ url: 'api/job',
+ sort: ['jobSort,asc', 'id,desc'],
+ crudMethod: { ...crudJob }
+ })
+ },
+ mixins: [presenter()],
+ // 数据字典
+ dicts: ['job_status'],
+ data() {
+ return {
+ permission: {
+ add: ['admin', 'job:add'],
+ edit: ['admin', 'job:edit'],
+ del: ['admin', 'job:del']
+ }
+ }
+ },
+ methods: {
+ // 改变状态
+ changeEnabled(data, val) {
+ this.$confirm('此操作将 "' + this.dict.label.job_status[val] + '" ' + data.name + '岗位, 是否继续?', '提示', {
+ confirmButtonText: '确定',
+ cancelButtonText: '取消',
+ type: 'warning'
+ }).then(() => {
+ // eslint-disable-next-line no-undef
+ crudJob.edit(data).then(() => {
+ // eslint-disable-next-line no-undef
+ this.crud.notify(this.dict.label.job_status[val] + '成功', 'success')
+ }).catch(err => {
+ data.enabled = !data.enabled
+ console.log(err.data.message)
+ })
+ }).catch(() => {
+ data.enabled = !data.enabled
+ })
+ }
+ }
+}
+</script>
+
+<style rel="stylesheet/scss" lang="scss" scoped>
+ ::v-deep .el-input-number .el-input__inner {
+ text-align: left;
+ }
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/system/job/module/form.vue b/UI source code/dns_mapping_ui-master/src/views/system/job/module/form.vue
new file mode 100644
index 0000000..aa538fb
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/system/job/module/form.vue
@@ -0,0 +1,110 @@
+<template>
+ <el-dialog
+ append-to-body
+ :close-on-click-modal="false"
+ :before-close="crud.cancelCU"
+ :visible="crud.status.cu > 0"
+ :title="crud.status.title"
+ width="500px"
+ >
+ <el-form
+ ref="form"
+ :model="form"
+ :rules="rules"
+ size="small"
+ label-width="80px"
+ >
+ <el-form-item
+ label="名称"
+ prop="name"
+ >
+ <el-input
+ v-model="form.name"
+ style="width: 370px;"
+ />
+ </el-form-item>
+ <el-form-item
+ label="排序"
+ prop="jobSort"
+ >
+ <el-input-number
+ v-model.number="form.jobSort"
+ :min="0"
+ :max="999"
+ controls-position="right"
+ style="width: 370px;"
+ />
+ </el-form-item>
+ <el-form-item
+ v-if="form.pid !== 0"
+ label="状态"
+ prop="enabled"
+ >
+ <el-radio
+ v-for="item in jobStatus"
+ :key="item.id"
+ v-model="form.enabled"
+ :label="item.value === 'true'"
+ >
+ {{ item.label }}
+ </el-radio>
+ </el-form-item>
+ </el-form>
+ <div
+ slot="footer"
+ class="dialog-footer"
+ >
+ <el-button
+ type="text"
+ @click="crud.cancelCU"
+ >
+ 取消
+ </el-button>
+ <el-button
+ :loading="crud.status.cu === 2"
+ type="primary"
+ @click="crud.submitCU"
+ >
+ 确认
+ </el-button>
+ </div>
+ </el-dialog>
+</template>
+
+<script>
+import { form } from '@crud/crud'
+
+const defaultForm = {
+ id: null,
+ name: '',
+ jobSort: 999,
+ enabled: true
+}
+export default {
+ mixins: [form(defaultForm)],
+ props: {
+ jobStatus: {
+ type: Array,
+ required: true
+ }
+ },
+ data() {
+ return {
+ rules: {
+ name: [
+ { required: true, message: '请输入名称', trigger: 'blur' }
+ ],
+ jobSort: [
+ { required: true, message: '请输入序号', trigger: 'blur', type: 'number' }
+ ]
+ }
+ }
+ }
+}
+</script>
+
+<style rel="stylesheet/scss" lang="scss" scoped>
+ ::v-deep .el-input-number .el-input__inner {
+ text-align: left;
+ }
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/system/job/module/header.vue b/UI source code/dns_mapping_ui-master/src/views/system/job/module/header.vue
new file mode 100644
index 0000000..6503317
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/system/job/module/header.vue
@@ -0,0 +1,32 @@
+<template>
+ <div
+ v-if="crud.props.searchToggle"
+ >
+ <el-input v-model="query.name" clearable size="small" placeholder="输入岗位名称搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
+ <date-range-picker v-model="query.createTime" class="date-item" />
+ <el-select v-model="query.enabled" clearable size="small" placeholder="状态" class="filter-item" style="width: 90px" @change="crud.toQuery">
+ <el-option v-for="item in dict.dict.job_status" :key="item.value" :label="item.label" :value="item.value" />
+ </el-select>
+ <rrOperation />
+ </div>
+</template>
+
+<script>
+import { header } from '@crud/crud'
+import rrOperation from '@crud/RR.operation'
+import DateRangePicker from '@/components/DateRangePicker'
+export default {
+ components: { rrOperation, DateRangePicker },
+ mixins: [header()],
+ props: {
+ dict: {
+ type: Object,
+ required: true
+ },
+ permission: {
+ type: Object,
+ required: true
+ }
+ }
+}
+</script>
diff --git a/UI source code/dns_mapping_ui-master/src/views/system/menu/index.vue b/UI source code/dns_mapping_ui-master/src/views/system/menu/index.vue
new file mode 100644
index 0000000..e4800e1
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/system/menu/index.vue
@@ -0,0 +1,252 @@
+<template>
+ <div class="app-container">
+ <!--工具栏-->
+ <div class="head-container">
+ <div v-if="crud.props.searchToggle">
+ <!-- 搜索 -->
+ <el-input v-model="query.blurry" clearable size="small" placeholder="模糊搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
+ <date-range-picker v-model="query.createTime" class="date-item" />
+ <rrOperation />
+ </div>
+ <crudOperation :permission="permission" />
+ </div>
+ <!--表单渲染-->
+ <el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="580px">
+ <el-form ref="form" :inline="true" :model="form" :rules="rules" size="small" label-width="80px">
+ <el-form-item label="菜单类型" prop="type">
+ <el-radio-group v-model="form.type" size="mini" style="width: 178px">
+ <el-radio-button label="0">目录</el-radio-button>
+ <el-radio-button label="1">菜单</el-radio-button>
+ <el-radio-button label="2">按钮</el-radio-button>
+ </el-radio-group>
+ </el-form-item>
+ <el-form-item v-show="form.type.toString() !== '2'" label="菜单图标" prop="icon">
+ <el-popover
+ placement="bottom-start"
+ width="450"
+ trigger="click"
+ @show="$refs['iconSelect'].reset()"
+ >
+ <IconSelect ref="iconSelect" @selected="selected" />
+ <el-input slot="reference" v-model="form.icon" style="width: 450px;" placeholder="点击选择图标" readonly>
+ <svg-icon v-if="form.icon" slot="prefix" :icon-class="form.icon" class="el-input__icon" style="height: 32px;width: 16px;" />
+ <i v-else slot="prefix" class="el-icon-search el-input__icon" />
+ </el-input>
+ </el-popover>
+ </el-form-item>
+ <el-form-item v-show="form.type.toString() !== '2'" label="外链菜单" prop="iFrame">
+ <el-radio-group v-model="form.iFrame" size="mini">
+ <el-radio-button label="true">是</el-radio-button>
+ <el-radio-button label="false">否</el-radio-button>
+ </el-radio-group>
+ </el-form-item>
+ <el-form-item v-show="form.type.toString() === '1'" label="菜单缓存" prop="cache">
+ <el-radio-group v-model="form.cache" size="mini">
+ <el-radio-button label="true">是</el-radio-button>
+ <el-radio-button label="false">否</el-radio-button>
+ </el-radio-group>
+ </el-form-item>
+ <el-form-item v-show="form.type.toString() !== '2'" label="菜单可见" prop="hidden">
+ <el-radio-group v-model="form.hidden" size="mini">
+ <el-radio-button label="false">是</el-radio-button>
+ <el-radio-button label="true">否</el-radio-button>
+ </el-radio-group>
+ </el-form-item>
+ <el-form-item v-if="form.type.toString() !== '2'" label="菜单标题" prop="title">
+ <el-input v-model="form.title" :style=" form.type.toString() === '0' ? 'width: 450px' : 'width: 178px'" placeholder="菜单标题" />
+ </el-form-item>
+ <el-form-item v-if="form.type.toString() === '2'" label="按钮名称" prop="title">
+ <el-input v-model="form.title" placeholder="按钮名称" style="width: 178px;" />
+ </el-form-item>
+ <el-form-item v-show="form.type.toString() !== '0'" label="权限标识" prop="permission">
+ <el-input v-model="form.permission" :disabled="form.iFrame.toString() === 'true'" placeholder="权限标识" style="width: 178px;" />
+ </el-form-item>
+ <el-form-item v-if="form.type.toString() !== '2'" label="路由地址" prop="path">
+ <el-input v-model="form.path" placeholder="路由地址" style="width: 178px;" />
+ </el-form-item>
+ <el-form-item label="菜单排序" prop="menuSort">
+ <el-input-number v-model.number="form.menuSort" :min="0" :max="999" controls-position="right" style="width: 178px;" />
+ </el-form-item>
+ <el-form-item v-show="form.iFrame.toString() !== 'true' && form.type.toString() === '1'" label="组件名称" prop="componentName">
+ <el-input v-model="form.componentName" style="width: 178px;" placeholder="匹配组件内Name字段" />
+ </el-form-item>
+ <el-form-item v-show="form.iFrame.toString() !== 'true' && form.type.toString() === '1'" label="组件路径" prop="component">
+ <el-input v-model="form.component" style="width: 178px;" placeholder="组件路径" />
+ </el-form-item>
+ <el-form-item label="上级类目" prop="pid">
+ <treeselect
+ v-model="form.pid"
+ :options="menus"
+ :load-options="loadMenus"
+ style="width: 450px;"
+ placeholder="选择上级类目"
+ />
+ </el-form-item>
+ </el-form>
+ <div slot="footer" class="dialog-footer">
+ <el-button type="text" @click="crud.cancelCU">取消</el-button>
+ <el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
+ </div>
+ </el-dialog>
+ <!--表格渲染-->
+ <el-table
+ ref="table"
+ v-loading="crud.loading"
+ lazy
+ :load="getMenus"
+ :data="crud.data"
+ :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
+ row-key="id"
+ @select="crud.selectChange"
+ @select-all="crud.selectAllChange"
+ @selection-change="crud.selectionChangeHandler"
+ >
+ <el-table-column type="selection" width="55" />
+ <el-table-column :show-overflow-tooltip="true" label="菜单标题" width="125px" prop="title" />
+ <el-table-column prop="icon" label="图标" align="center" width="60px">
+ <template slot-scope="scope">
+ <svg-icon :icon-class="scope.row.icon ? scope.row.icon : ''" />
+ </template>
+ </el-table-column>
+ <el-table-column prop="menuSort" align="center" label="排序">
+ <template slot-scope="scope">
+ {{ scope.row.menuSort }}
+ </template>
+ </el-table-column>
+ <el-table-column :show-overflow-tooltip="true" prop="permission" label="权限标识" />
+ <el-table-column :show-overflow-tooltip="true" prop="component" label="组件路径" />
+ <el-table-column prop="iFrame" label="外链" width="75px">
+ <template slot-scope="scope">
+ <span v-if="scope.row.iFrame">是</span>
+ <span v-else>否</span>
+ </template>
+ </el-table-column>
+ <el-table-column prop="cache" label="缓存" width="75px">
+ <template slot-scope="scope">
+ <span v-if="scope.row.cache">是</span>
+ <span v-else>否</span>
+ </template>
+ </el-table-column>
+ <el-table-column prop="hidden" label="可见" width="75px">
+ <template slot-scope="scope">
+ <span v-if="scope.row.hidden">否</span>
+ <span v-else>是</span>
+ </template>
+ </el-table-column>
+ <el-table-column prop="createTime" label="创建日期" width="135px" />
+ <el-table-column v-if="checkPer(['admin','menu:edit','menu:del'])" label="操作" width="130px" align="center" fixed="right">
+ <template slot-scope="scope">
+ <udOperation
+ :data="scope.row"
+ :permission="permission"
+ msg="确定删除吗,如果存在下级节点则一并删除,此操作不能撤销!"
+ />
+ </template>
+ </el-table-column>
+ </el-table>
+ </div>
+</template>
+
+<script>
+import crudMenu from '@/api/system/menu'
+import IconSelect from '@/components/IconSelect'
+import Treeselect from '@riophae/vue-treeselect'
+import '@riophae/vue-treeselect/dist/vue-treeselect.css'
+import { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect'
+import CRUD, { presenter, header, form, crud } from '@crud/crud'
+import rrOperation from '@crud/RR.operation'
+import crudOperation from '@crud/CRUD.operation'
+import udOperation from '@crud/UD.operation'
+import DateRangePicker from '@/components/DateRangePicker'
+
+// crud交由presenter持有
+const defaultForm = { id: null, title: null, menuSort: 999, path: null, component: null, componentName: null, iFrame: false, roles: [], pid: 0, icon: null, cache: false, hidden: false, type: 0, permission: null }
+export default {
+ name: 'Menu',
+ components: { Treeselect, IconSelect, crudOperation, rrOperation, udOperation, DateRangePicker },
+ cruds() {
+ return CRUD({ title: '菜单', url: 'api/menus', crudMethod: { ...crudMenu }})
+ },
+ mixins: [presenter(), header(), form(defaultForm), crud()],
+ data() {
+ return {
+ menus: [],
+ permission: {
+ add: ['admin', 'menu:add'],
+ edit: ['admin', 'menu:edit'],
+ del: ['admin', 'menu:del']
+ },
+ rules: {
+ title: [
+ { required: true, message: '请输入标题', trigger: 'blur' }
+ ],
+ path: [
+ { required: true, message: '请输入地址', trigger: 'blur' }
+ ]
+ }
+ }
+ },
+ methods: {
+ // 新增与编辑前做的操作
+ [CRUD.HOOK.afterToCU](crud, form) {
+ this.menus = []
+ if (form.id != null) {
+ if (form.pid === null) {
+ form.pid = 0
+ }
+ this.getSupDepts(form.id)
+ } else {
+ this.menus.push({ id: 0, label: '顶级类目', children: null })
+ }
+ },
+ getMenus(tree, treeNode, resolve) {
+ const params = { pid: tree.id }
+ setTimeout(() => {
+ crudMenu.getMenus(params).then(res => {
+ resolve(res.content)
+ })
+ }, 100)
+ },
+ getSupDepts(id) {
+ crudMenu.getMenuSuperior(id).then(res => {
+ const children = res.map(function(obj) {
+ if (!obj.leaf && !obj.children) {
+ obj.children = null
+ }
+ return obj
+ })
+ this.menus = [{ id: 0, label: '顶级类目', children: children }]
+ })
+ },
+ loadMenus({ action, parentNode, callback }) {
+ if (action === LOAD_CHILDREN_OPTIONS) {
+ crudMenu.getMenusTree(parentNode.id).then(res => {
+ parentNode.children = res.map(function(obj) {
+ if (!obj.leaf) {
+ obj.children = null
+ }
+ return obj
+ })
+ setTimeout(() => {
+ callback()
+ }, 100)
+ })
+ }
+ },
+ // 选中图标
+ selected(name) {
+ this.form.icon = name
+ }
+ }
+}
+</script>
+
+<style rel="stylesheet/scss" lang="scss" scoped>
+ ::v-deep .el-input-number .el-input__inner {
+ text-align: left;
+ }
+ ::v-deep .vue-treeselect__control,::v-deep .vue-treeselect__placeholder,::v-deep .vue-treeselect__single-value {
+ height: 30px;
+ line-height: 30px;
+ }
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/system/role/index.vue b/UI source code/dns_mapping_ui-master/src/views/system/role/index.vue
new file mode 100644
index 0000000..242ed1a
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/system/role/index.vue
@@ -0,0 +1,360 @@
+<template>
+ <div class="app-container">
+ <!--工具栏-->
+ <div class="head-container">
+ <div v-if="crud.props.searchToggle">
+ <!-- 搜索 -->
+ <el-input v-model="query.blurry" size="small" clearable placeholder="输入名称或者描述搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
+ <date-range-picker v-model="query.createTime" class="date-item" />
+ <rrOperation />
+ </div>
+ <crudOperation :permission="permission" />
+ </div>
+ <!-- 表单渲染 -->
+ <el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="520px">
+ <el-form ref="form" :inline="true" :model="form" :rules="rules" size="small" label-width="80px">
+ <el-form-item label="角色名称" prop="name">
+ <el-input v-model="form.name" style="width: 380px;" />
+ </el-form-item>
+ <el-form-item label="角色级别" prop="level">
+ <el-input-number v-model.number="form.level" :min="1" controls-position="right" style="width: 145px;" />
+ </el-form-item>
+ <el-form-item label="数据范围" prop="dataScope">
+ <el-select v-model="form.dataScope" style="width: 140px" placeholder="请选择数据范围" @change="changeScope">
+ <el-option
+ v-for="item in dateScopes"
+ :key="item"
+ :label="item"
+ :value="item"
+ />
+ </el-select>
+ </el-form-item>
+ <el-form-item v-if="form.dataScope === '自定义'" label="数据权限" prop="depts">
+ <treeselect
+ v-model="deptDatas"
+ :load-options="loadDepts"
+ :options="depts"
+ multiple
+ style="width: 380px"
+ placeholder="请选择"
+ />
+ </el-form-item>
+ <el-form-item label="描述信息" prop="description">
+ <el-input v-model="form.description" style="width: 380px;" rows="5" type="textarea" />
+ </el-form-item>
+ </el-form>
+ <div slot="footer" class="dialog-footer">
+ <el-button type="text" @click="crud.cancelCU">取消</el-button>
+ <el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
+ </div>
+ </el-dialog>
+ <el-row :gutter="15">
+ <!--角色管理-->
+ <el-col :xs="24" :sm="24" :md="16" :lg="16" :xl="17" style="margin-bottom: 10px">
+ <el-card class="box-card" shadow="never">
+ <div slot="header" class="clearfix">
+ <span class="role-span">角色列表</span>
+ </div>
+ <el-table ref="table" v-loading="crud.loading" highlight-current-row style="width: 100%;" :data="crud.data" @selection-change="crud.selectionChangeHandler" @current-change="handleCurrentChange">
+ <el-table-column :selectable="checkboxT" type="selection" width="55" />
+ <el-table-column prop="name" label="名称" />
+ <el-table-column prop="dataScope" label="数据权限" />
+ <el-table-column prop="level" label="角色级别" />
+ <el-table-column :show-overflow-tooltip="true" prop="description" label="描述" />
+ <el-table-column :show-overflow-tooltip="true" width="135px" prop="createTime" label="创建日期" />
+ <el-table-column v-if="checkPer(['admin','roles:edit','roles:del'])" label="操作" width="130px" align="center" fixed="right">
+ <template slot-scope="scope">
+ <udOperation
+ v-if="scope.row.level >= level"
+ :data="scope.row"
+ :permission="permission"
+ />
+ </template>
+ </el-table-column>
+ </el-table>
+ <!--分页组件-->
+ <pagination />
+ </el-card>
+ </el-col>
+ <!-- 菜单授权 -->
+ <el-col :xs="24" :sm="24" :md="8" :lg="8" :xl="7">
+ <el-card class="box-card" shadow="never">
+ <div slot="header" class="clearfix">
+ <el-tooltip class="item" effect="dark" content="选择指定角色分配菜单" placement="top">
+ <span class="role-span">菜单分配</span>
+ </el-tooltip>
+ <el-button
+ v-permission="['admin','roles:edit']"
+ :disabled="!showButton"
+ :loading="menuLoading"
+ icon="el-icon-check"
+ size="mini"
+ style="float: right; padding: 6px 9px"
+ type="primary"
+ @click="saveMenu"
+ >保存</el-button>
+ </div>
+ <el-tree
+ ref="menu"
+ lazy
+ :data="menus"
+ :default-checked-keys="menuIds"
+ :load="getMenuDatas"
+ :props="defaultProps"
+ check-strictly
+ accordion
+ show-checkbox
+ node-key="id"
+ @check="menuChange"
+ />
+ </el-card>
+ </el-col>
+ </el-row>
+ </div>
+</template>
+
+<script>
+import crudRoles from '@/api/system/role'
+import { getDepts, getDeptSuperior } from '@/api/system/dept'
+import { getMenusTree, getChild } from '@/api/system/menu'
+import CRUD, { presenter, header, form, crud } from '@crud/crud'
+import rrOperation from '@crud/RR.operation'
+import crudOperation from '@crud/CRUD.operation'
+import udOperation from '@crud/UD.operation'
+import pagination from '@crud/Pagination'
+import Treeselect from '@riophae/vue-treeselect'
+import '@riophae/vue-treeselect/dist/vue-treeselect.css'
+import { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect'
+import DateRangePicker from '@/components/DateRangePicker'
+
+const defaultForm = { id: null, name: null, depts: [], description: null, dataScope: '全部', level: 3 }
+export default {
+ name: 'Role',
+ components: { Treeselect, pagination, crudOperation, rrOperation, udOperation, DateRangePicker },
+ cruds() {
+ return CRUD({ title: '角色', url: 'api/roles', sort: 'level,asc', crudMethod: { ...crudRoles }})
+ },
+ mixins: [presenter(), header(), form(defaultForm), crud()],
+ data() {
+ return {
+ defaultProps: { children: 'children', label: 'label', isLeaf: 'leaf' },
+ dateScopes: ['全部', '本级', '自定义'], level: 3,
+ currentId: 0, menuLoading: false, showButton: false,
+ menus: [], menuIds: [], depts: [], deptDatas: [], // 多选时使用
+ permission: {
+ add: ['admin', 'roles:add'],
+ edit: ['admin', 'roles:edit'],
+ del: ['admin', 'roles:del']
+ },
+ rules: {
+ name: [
+ { required: true, message: '请输入名称', trigger: 'blur' }
+ ],
+ permission: [
+ { required: true, message: '请输入权限', trigger: 'blur' }
+ ]
+ }
+ }
+ },
+ created() {
+ crudRoles.getLevel().then(data => {
+ this.level = data.level
+ })
+ },
+ methods: {
+ getMenuDatas(node, resolve) {
+ setTimeout(() => {
+ getMenusTree(node.data.id ? node.data.id : 0).then(res => {
+ resolve(res)
+ })
+ }, 100)
+ },
+ [CRUD.HOOK.afterRefresh]() {
+ this.$refs.menu.setCheckedKeys([])
+ },
+ // 新增前初始化部门信息
+ [CRUD.HOOK.beforeToAdd](crud, form) {
+ this.deptDatas = []
+ form.menus = null
+ },
+ // 编辑前初始化自定义数据权限的部门信息
+ [CRUD.HOOK.beforeToEdit](crud, form) {
+ this.deptDatas = []
+ if (form.dataScope === '自定义') {
+ this.getSupDepts(form.depts)
+ }
+ const _this = this
+ form.depts.forEach(function(dept) {
+ _this.deptDatas.push(dept.id)
+ })
+ // 将角色的菜单清空,避免日志入库数据过长
+ form.menus = null
+ },
+ // 提交前做的操作
+ [CRUD.HOOK.afterValidateCU](crud) {
+ if (crud.form.dataScope === '自定义' && this.deptDatas.length === 0) {
+ this.$message({
+ message: '自定义数据权限不能为空',
+ type: 'warning'
+ })
+ return false
+ } else if (crud.form.dataScope === '自定义') {
+ const depts = []
+ this.deptDatas.forEach(function(data) {
+ const dept = { id: data }
+ depts.push(dept)
+ })
+ crud.form.depts = depts
+ } else {
+ crud.form.depts = []
+ }
+ return true
+ },
+ // 触发单选
+ handleCurrentChange(val) {
+ if (val) {
+ const _this = this
+ // 清空菜单的选中
+ this.$refs.menu.setCheckedKeys([])
+ // 保存当前的角色id
+ this.currentId = val.id
+ // 初始化默认选中的key
+ this.menuIds = []
+ val.menus.forEach(function(data) {
+ _this.menuIds.push(data.id)
+ })
+ this.showButton = true
+ }
+ },
+ menuChange(menu) {
+ // 获取该节点的所有子节点,id 包含自身
+ getChild(menu.id).then(childIds => {
+ // 判断是否在 menuIds 中,如果存在则删除,否则添加
+ if (this.menuIds.indexOf(menu.id) !== -1) {
+ for (let i = 0; i < childIds.length; i++) {
+ const index = this.menuIds.indexOf(childIds[i])
+ if (index !== -1) {
+ this.menuIds.splice(index, 1)
+ }
+ }
+ } else {
+ for (let i = 0; i < childIds.length; i++) {
+ const index = this.menuIds.indexOf(childIds[i])
+ if (index === -1) {
+ this.menuIds.push(childIds[i])
+ }
+ }
+ }
+ this.$refs.menu.setCheckedKeys(this.menuIds)
+ })
+ },
+ // 保存菜单
+ saveMenu() {
+ this.menuLoading = true
+ const role = { id: this.currentId, menus: [] }
+ // 得到已选中的 key 值
+ this.menuIds.forEach(function(id) {
+ const menu = { id: id }
+ role.menus.push(menu)
+ })
+ crudRoles.editMenu(role).then(() => {
+ this.crud.notify('保存成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
+ this.menuLoading = false
+ this.update()
+ }).catch(err => {
+ this.menuLoading = false
+ console.log(err.response.data.message)
+ })
+ },
+ // 改变数据
+ update() {
+ // 无刷新更新 表格数据
+ crudRoles.get(this.currentId).then(res => {
+ for (let i = 0; i < this.crud.data.length; i++) {
+ if (res.id === this.crud.data[i].id) {
+ this.crud.data[i] = res
+ break
+ }
+ }
+ })
+ },
+ // 获取部门数据
+ getDepts() {
+ getDepts({ enabled: true }).then(res => {
+ this.depts = res.content.map(function(obj) {
+ if (obj.hasChildren) {
+ obj.children = null
+ }
+ return obj
+ })
+ })
+ },
+ getSupDepts(depts) {
+ const ids = []
+ depts.forEach(dept => {
+ ids.push(dept.id)
+ })
+ getDeptSuperior(ids).then(res => {
+ const date = res.content
+ this.buildDepts(date)
+ this.depts = date
+ })
+ },
+ buildDepts(depts) {
+ depts.forEach(data => {
+ if (data.children) {
+ this.buildDepts(data.children)
+ }
+ if (data.hasChildren && !data.children) {
+ data.children = null
+ }
+ })
+ },
+ // 获取弹窗内部门数据
+ loadDepts({ action, parentNode, callback }) {
+ if (action === LOAD_CHILDREN_OPTIONS) {
+ getDepts({ enabled: true, pid: parentNode.id }).then(res => {
+ parentNode.children = res.content.map(function(obj) {
+ if (obj.hasChildren) {
+ obj.children = null
+ }
+ return obj
+ })
+ setTimeout(() => {
+ callback()
+ }, 200)
+ })
+ }
+ },
+ // 如果数据权限为自定义则获取部门数据
+ changeScope() {
+ if (this.form.dataScope === '自定义') {
+ this.getDepts()
+ }
+ },
+ checkboxT(row) {
+ return row.level >= this.level
+ }
+ }
+}
+</script>
+
+<style rel="stylesheet/scss" lang="scss">
+ .role-span {
+ font-weight: bold;color: #303133;
+ font-size: 15px;
+ }
+</style>
+
+<style rel="stylesheet/scss" lang="scss" scoped>
+ ::v-deep .el-input-number .el-input__inner {
+ text-align: left;
+ }
+ ::v-deep .vue-treeselect__multi-value{
+ margin-bottom: 0;
+ }
+ ::v-deep .vue-treeselect__multi-value-item{
+ border: 0;
+ padding: 0;
+ }
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/system/timing/index.vue b/UI source code/dns_mapping_ui-master/src/views/system/timing/index.vue
new file mode 100644
index 0000000..bb3bf77
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/system/timing/index.vue
@@ -0,0 +1,210 @@
+<template>
+ <div class="app-container">
+ <!--工具栏-->
+ <div class="head-container">
+ <div v-if="crud.props.searchToggle">
+ <!-- 搜索 -->
+ <el-input v-model="query.jobName" clearable size="small" placeholder="输入任务名称搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="toQuery" />
+ <date-range-picker v-model="query.createTime" class="date-item" />
+ <rrOperation />
+ </div>
+ <crudOperation :permission="permission">
+ <!-- 任务日志 -->
+ <el-button
+ slot="right"
+ class="filter-item"
+ size="mini"
+ type="info"
+ icon="el-icon-tickets"
+ @click="doLog"
+ >日志</el-button>
+ </crudOperation>
+ <Log ref="log" />
+ </div>
+ <!--Form表单-->
+ <el-dialog :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" append-to-body width="730px">
+ <el-form ref="form" :inline="true" :model="form" :rules="rules" size="small" label-width="100px">
+ <el-form-item label="任务名称" prop="jobName">
+ <el-input v-model="form.jobName" style="width: 220px;" />
+ </el-form-item>
+ <el-form-item label="任务描述" prop="description">
+ <el-input v-model="form.description" style="width: 220px;" />
+ </el-form-item>
+ <el-form-item label="Bean名称" prop="beanName">
+ <el-input v-model="form.beanName" style="width: 220px;" />
+ </el-form-item>
+ <el-form-item label="执行方法" prop="methodName">
+ <el-input v-model="form.methodName" style="width: 220px;" />
+ </el-form-item>
+ <el-form-item label="Cron表达式" prop="cronExpression">
+ <el-input v-model="form.cronExpression" style="width: 220px;" />
+ </el-form-item>
+ <el-form-item label="子任务ID">
+ <el-input v-model="form.subTask" placeholder="多个用逗号隔开,按顺序执行" style="width: 220px;" />
+ </el-form-item>
+ <el-form-item label="任务负责人" prop="personInCharge">
+ <el-input v-model="form.personInCharge" style="width: 220px;" />
+ </el-form-item>
+ <el-form-item label="告警邮箱" prop="email">
+ <el-input v-model="form.email" placeholder="多个邮箱用逗号隔开" style="width: 220px;" />
+ </el-form-item>
+ <el-form-item label="失败后暂停">
+ <el-radio-group v-model="form.pauseAfterFailure" style="width: 220px">
+ <el-radio :label="true">是</el-radio>
+ <el-radio :label="false">否</el-radio>
+ </el-radio-group>
+ </el-form-item>
+ <el-form-item label="任务状态">
+ <el-radio-group v-model="form.isPause" style="width: 220px">
+ <el-radio :label="false">启用</el-radio>
+ <el-radio :label="true">暂停</el-radio>
+ </el-radio-group>
+ </el-form-item>
+ <el-form-item label="参数内容">
+ <el-input v-model="form.params" style="width: 556px;" rows="4" type="textarea" />
+ </el-form-item>
+ </el-form>
+ <div slot="footer" class="dialog-footer">
+ <el-button type="text" @click="crud.cancelCU">取消</el-button>
+ <el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
+ </div>
+ </el-dialog>
+ <!--表格渲染-->
+ <el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
+ <el-table-column :selectable="checkboxT" type="selection" width="55" />
+ <el-table-column :show-overflow-tooltip="true" prop="id" label="任务ID" />
+ <el-table-column :show-overflow-tooltip="true" prop="jobName" label="任务名称" />
+ <el-table-column :show-overflow-tooltip="true" prop="beanName" label="Bean名称" />
+ <el-table-column :show-overflow-tooltip="true" prop="methodName" label="执行方法" />
+ <el-table-column :show-overflow-tooltip="true" prop="params" label="参数" />
+ <el-table-column :show-overflow-tooltip="true" prop="cronExpression" label="cron表达式" />
+ <el-table-column :show-overflow-tooltip="true" prop="isPause" width="90px" label="状态">
+ <template slot-scope="scope">
+ <el-tag :type="scope.row.isPause ? 'warning' : 'success'">{{ scope.row.isPause ? '已暂停' : '运行中' }}</el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column :show-overflow-tooltip="true" prop="description" width="150px" label="描述" />
+ <el-table-column :show-overflow-tooltip="true" prop="createTime" width="136px" label="创建日期" />
+ <el-table-column v-if="checkPer(['admin','timing:edit','timing:del'])" label="操作" width="170px" align="center" fixed="right">
+ <template slot-scope="scope">
+ <el-button v-permission="['admin','timing:edit']" size="mini" style="margin-right: 3px;" type="text" @click="crud.toEdit(scope.row)">编辑</el-button>
+ <el-button v-permission="['admin','timing:edit']" style="margin-left: -2px" type="text" size="mini" @click="execute(scope.row.id)">执行</el-button>
+ <el-button v-permission="['admin','timing:edit']" style="margin-left: 3px" type="text" size="mini" @click="updateStatus(scope.row.id,scope.row.isPause ? '恢复' : '暂停')">
+ {{ scope.row.isPause ? '恢复' : '暂停' }}
+ </el-button>
+ <el-popover
+ :ref="scope.row.id"
+ v-permission="['admin','timing:del']"
+ placement="top"
+ width="200"
+ >
+ <p>确定停止并删除该任务吗?</p>
+ <div style="text-align: right; margin: 0">
+ <el-button size="mini" type="text" @click="$refs[scope.row.id].doClose()">取消</el-button>
+ <el-button :loading="delLoading" type="primary" size="mini" @click="delMethod(scope.row.id)">确定</el-button>
+ </div>
+ <el-button slot="reference" type="text" size="mini">删除</el-button>
+ </el-popover>
+ </template>
+ </el-table-column>
+ </el-table>
+ <!--分页组件-->
+ <pagination />
+ </div>
+</template>
+
+<script>
+import crudJob from '@/api/system/timing'
+import Log from './log'
+import CRUD, { presenter, header, form, crud } from '@crud/crud'
+import rrOperation from '@crud/RR.operation'
+import crudOperation from '@crud/CRUD.operation'
+import pagination from '@crud/Pagination'
+import DateRangePicker from '@/components/DateRangePicker'
+
+const defaultForm = { id: null, jobName: null, subTask: null, beanName: null, methodName: null, params: null, cronExpression: null, pauseAfterFailure: true, isPause: false, personInCharge: null, email: null, description: null }
+export default {
+ name: 'Timing',
+ components: { Log, pagination, crudOperation, rrOperation, DateRangePicker },
+ cruds() {
+ return CRUD({ title: '定时任务', url: 'api/jobs', crudMethod: { ...crudJob }})
+ },
+ mixins: [presenter(), header(), form(defaultForm), crud()],
+ data() {
+ return {
+ delLoading: false,
+ permission: {
+ add: ['admin', 'timing:add'],
+ edit: ['admin', 'timing:edit'],
+ del: ['admin', 'timing:del']
+ },
+ rules: {
+ jobName: [
+ { required: true, message: '请输入任务名称', trigger: 'blur' }
+ ],
+ description: [
+ { required: true, message: '请输入任务描述', trigger: 'blur' }
+ ],
+ beanName: [
+ { required: true, message: '请输入Bean名称', trigger: 'blur' }
+ ],
+ methodName: [
+ { required: true, message: '请输入方法名称', trigger: 'blur' }
+ ],
+ cronExpression: [
+ { required: true, message: '请输入Cron表达式', trigger: 'blur' }
+ ],
+ personInCharge: [
+ { required: true, message: '请输入负责人名称', trigger: 'blur' }
+ ]
+ }
+ }
+ },
+ methods: {
+ // 执行
+ execute(id) {
+ crudJob.execution(id).then(res => {
+ this.crud.notify('执行成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
+ }).catch(err => {
+ console.log(err.response.data.message)
+ })
+ },
+ // 改变状态
+ updateStatus(id, status) {
+ if (status === '恢复') {
+ this.updateParams(id)
+ }
+ crudJob.updateIsPause(id).then(res => {
+ this.crud.toQuery()
+ this.crud.notify(status + '成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
+ }).catch(err => {
+ console.log(err.response.data.message)
+ })
+ },
+ updateParams(id) {
+ console.log(id)
+ },
+ delMethod(id) {
+ this.delLoading = true
+ crudJob.del([id]).then(() => {
+ this.delLoading = false
+ this.$refs[id].doClose()
+ this.crud.dleChangePage(1)
+ this.crud.delSuccessNotify()
+ this.crud.toQuery()
+ }).catch(() => {
+ this.delLoading = false
+ this.$refs[id].doClose()
+ })
+ },
+ // 显示日志
+ doLog() {
+ this.$refs.log.dialog = true
+ this.$refs.log.doInit()
+ },
+ checkboxT(row, rowIndex) {
+ return row.id !== 1
+ }
+ }
+}
+</script>
diff --git a/UI source code/dns_mapping_ui-master/src/views/system/timing/log.vue b/UI source code/dns_mapping_ui-master/src/views/system/timing/log.vue
new file mode 100644
index 0000000..09c32ef
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/system/timing/log.vue
@@ -0,0 +1,104 @@
+<template>
+ <el-dialog :visible.sync="dialog" append-to-body title="执行日志" width="88%">
+ <!-- 搜索 -->
+ <div class="head-container">
+ <el-input v-model="query.jobName" clearable size="small" placeholder="输入任务名称搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="toQuery" />
+ <date-range-picker v-model="query.createTime" class="date-item" />
+ <el-select v-model="query.isSuccess" placeholder="日志状态" clearable size="small" class="filter-item" style="width: 110px" @change="toQuery">
+ <el-option v-for="item in enabledTypeOptions" :key="item.key" :label="item.display_name" :value="item.key" />
+ </el-select>
+ <el-button class="filter-item" size="mini" type="success" icon="el-icon-search" @click="toQuery">搜索</el-button>
+ <!-- 导出 -->
+ <div style="display: inline-block;">
+ <el-button
+ :loading="downloadLoading"
+ size="mini"
+ class="filter-item"
+ type="warning"
+ icon="el-icon-download"
+ @click="downloadMethod"
+ >导出</el-button>
+ </div>
+ </div>
+ <!--表格渲染-->
+ <el-table v-loading="loading" :data="data" style="width: 100%;margin-top: -10px;">
+ <el-table-column :show-overflow-tooltip="true" prop="jobName" label="任务名称" />
+ <el-table-column :show-overflow-tooltip="true" prop="beanName" label="Bean名称" />
+ <el-table-column :show-overflow-tooltip="true" prop="methodName" label="执行方法" />
+ <el-table-column :show-overflow-tooltip="true" prop="params" width="120px" label="参数" />
+ <el-table-column :show-overflow-tooltip="true" prop="cronExpression" label="cron表达式" />
+ <el-table-column prop="createTime" label="异常详情" width="110px">
+ <template slot-scope="scope">
+ <el-button v-show="scope.row.exceptionDetail" size="mini" type="text" @click="info(scope.row.exceptionDetail)">查看详情</el-button>
+ </template>
+ </el-table-column>
+ <el-table-column :show-overflow-tooltip="true" align="center" prop="time" width="100px" label="耗时(毫秒)" />
+ <el-table-column align="center" prop="isSuccess" width="80px" label="状态">
+ <template slot-scope="scope">
+ <el-tag :type="scope.row.isSuccess ? 'success' : 'danger'">{{ scope.row.isSuccess ? '成功' : '失败' }}</el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column :show-overflow-tooltip="true" prop="createTime" label="创建日期" />
+ </el-table>
+ <el-dialog :visible.sync="errorDialog" append-to-body title="异常详情" width="85%">
+ <pre v-highlightjs="errorInfo"><code class="java" /></pre>
+ </el-dialog>
+ <!--分页组件-->
+ <el-pagination
+ :total="total"
+ :current-page="page + 1"
+ :page-size="6"
+ style="margin-top:8px;"
+ layout="total, prev, pager, next"
+ @size-change="sizeChange"
+ @current-change="pageChange"
+ />
+ </el-dialog>
+</template>
+
+<script>
+import crud from '@/mixins/crud'
+import DateRangePicker from '@/components/DateRangePicker'
+export default {
+ components: { DateRangePicker },
+ mixins: [crud],
+ data() {
+ return {
+ title: '任务日志',
+ errorInfo: '', errorDialog: false,
+ enabledTypeOptions: [
+ { key: 'true', display_name: '成功' },
+ { key: 'false', display_name: '失败' }
+ ]
+ }
+ },
+ methods: {
+ doInit() {
+ this.$nextTick(() => {
+ this.init()
+ })
+ },
+ // 获取数据前设置好接口地址
+ beforeInit() {
+ this.url = 'api/jobs/logs'
+ this.size = 6
+ return true
+ },
+ // 异常详情
+ info(errorInfo) {
+ this.errorInfo = errorInfo
+ this.errorDialog = true
+ }
+ }
+}
+</script>
+
+<style scoped>
+ .java.hljs{
+ color: #444;
+ background: #ffffff !important;
+ }
+ ::v-deep .el-dialog__body{
+ padding: 0 20px 10px 20px !important;
+ }
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/system/user/center.vue b/UI source code/dns_mapping_ui-master/src/views/system/user/center.vue
new file mode 100644
index 0000000..1cc5681
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/system/user/center.vue
@@ -0,0 +1,221 @@
+<template>
+ <div class="app-container">
+ <el-row :gutter="20">
+ <el-col :xs="24" :sm="24" :md="8" :lg="6" :xl="5" style="margin-bottom: 10px">
+ <el-card class="box-card">
+ <div slot="header" class="clearfix">
+ <span>个人信息</span>
+ </div>
+ <div>
+ <div style="text-align: center">
+ <div class="el-upload">
+ <img :src="user.avatarName ? baseApi + '/avatar/' + user.avatarName : Avatar" title="点击上传头像" class="avatar" @click="toggleShow">
+ <myUpload
+ v-model="show"
+ :headers="headers"
+ :url="updateAvatarApi"
+ @crop-upload-success="cropUploadSuccess"
+ />
+ </div>
+ </div>
+ <ul class="user-info">
+ <li><div style="height: 100%"><svg-icon icon-class="login" /> 登录账号<div class="user-right">{{ user.username }}</div></div></li>
+ <li><svg-icon icon-class="user1" /> 用户昵称 <div class="user-right">{{ user.nickName }}</div></li>
+ <li><svg-icon icon-class="dept" /> 所属部门 <div class="user-right"> {{ user.dept.name }}</div></li>
+ <li><svg-icon icon-class="phone" /> 手机号码 <div class="user-right">{{ user.phone }}</div></li>
+ <li><svg-icon icon-class="email" /> 用户邮箱 <div class="user-right">{{ user.email }}</div></li>
+ <li>
+ <svg-icon icon-class="anq" /> 安全设置
+ <div class="user-right">
+ <a @click="$refs.pass.dialog = true">修改密码</a>
+ <a @click="$refs.email.dialog = true">修改邮箱</a>
+ </div>
+ </li>
+ </ul>
+ </div>
+ </el-card>
+ </el-col>
+ <el-col :xs="24" :sm="24" :md="16" :lg="18" :xl="19">
+ <!-- 用户资料 -->
+ <el-card class="box-card">
+ <el-tabs v-model="activeName" @tab-click="handleClick">
+ <el-tab-pane label="用户资料" name="first">
+ <el-form ref="form" :model="form" :rules="rules" style="margin-top: 10px;" size="small" label-width="65px">
+ <el-form-item label="昵称" prop="nickName">
+ <el-input v-model="form.nickName" style="width: 35%" />
+ <span style="color: #C0C0C0;margin-left: 10px;">用户昵称不作为登录使用</span>
+ </el-form-item>
+ <el-form-item label="手机号" prop="phone">
+ <el-input v-model="form.phone" style="width: 35%;" />
+ <span style="color: #C0C0C0;margin-left: 10px;">手机号码不能重复</span>
+ </el-form-item>
+ <el-form-item label="性别">
+ <el-radio-group v-model="form.gender" style="width: 178px">
+ <el-radio label="男">男</el-radio>
+ <el-radio label="女">女</el-radio>
+ </el-radio-group>
+ </el-form-item>
+ <el-form-item label="">
+ <el-button :loading="saveLoading" size="mini" type="primary" @click="doSubmit">保存配置</el-button>
+ </el-form-item>
+ </el-form>
+ </el-tab-pane>
+ <!-- 操作日志 -->
+ <el-tab-pane label="操作日志" name="second">
+ <el-table v-loading="loading" :data="data" style="width: 100%;">
+ <el-table-column prop="description" label="行为" />
+ <el-table-column prop="requestIp" label="IP" />
+ <el-table-column :show-overflow-tooltip="true" prop="address" label="IP来源" />
+ <el-table-column prop="browser" label="浏览器" />
+ <el-table-column prop="time" label="请求耗时" align="center">
+ <template slot-scope="scope">
+ <el-tag v-if="scope.row.time <= 300">{{ scope.row.time }}ms</el-tag>
+ <el-tag v-else-if="scope.row.time <= 1000" type="warning">{{ scope.row.time }}ms</el-tag>
+ <el-tag v-else type="danger">{{ scope.row.time }}ms</el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column
+ align="right"
+ >
+ <template slot="header">
+ <div style="display:inline-block;float: right;cursor: pointer" @click="init">创建日期<i class="el-icon-refresh" style="margin-left: 40px" /></div>
+ </template>
+ <template slot-scope="scope">
+ <span>{{ scope.row.createTime }}</span>
+ </template>
+ </el-table-column>
+ </el-table>
+ <!--分页组件-->
+ <el-pagination
+ :total="total"
+ :current-page="page + 1"
+ style="margin-top: 8px;"
+ layout="total, prev, pager, next, sizes"
+ @size-change="sizeChange"
+ @current-change="pageChange"
+ />
+ </el-tab-pane>
+ </el-tabs>
+ </el-card>
+ </el-col>
+ </el-row>
+ <updateEmail ref="email" :email="user.email" />
+ <updatePass ref="pass" />
+ </div>
+</template>
+
+<script>
+import myUpload from 'vue-image-crop-upload'
+import { mapGetters } from 'vuex'
+import updatePass from './center/updatePass'
+import updateEmail from './center/updateEmail'
+import { getToken } from '@/utils/auth'
+import store from '@/store'
+import { isvalidPhone } from '@/utils/validate'
+import crud from '@/mixins/crud'
+import { editUser } from '@/api/system/user'
+import Avatar from '@/assets/images/avatar.png'
+export default {
+ name: 'Center',
+ components: { updatePass, updateEmail, myUpload },
+ mixins: [crud],
+ data() {
+ // 自定义验证
+ const validPhone = (rule, value, callback) => {
+ if (!value) {
+ callback(new Error('请输入电话号码'))
+ } else if (!isvalidPhone(value)) {
+ callback(new Error('请输入正确的11位手机号码'))
+ } else {
+ callback()
+ }
+ }
+ return {
+ show: false,
+ Avatar: Avatar,
+ activeName: 'first',
+ saveLoading: false,
+ headers: {
+ 'Authorization': getToken()
+ },
+ form: {},
+ rules: {
+ nickName: [
+ { required: true, message: '请输入用户昵称', trigger: 'blur' },
+ { min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
+ ],
+ phone: [
+ { required: true, trigger: 'blur', validator: validPhone }
+ ]
+ }
+ }
+ },
+ computed: {
+ ...mapGetters([
+ 'user',
+ 'updateAvatarApi',
+ 'baseApi'
+ ])
+ },
+ created() {
+ this.form = { id: this.user.id, nickName: this.user.nickName, gender: this.user.gender, phone: this.user.phone }
+ store.dispatch('GetInfo').then(() => {})
+ },
+ methods: {
+ toggleShow() {
+ this.show = !this.show
+ },
+ handleClick(tab, event) {
+ if (tab.name === 'second') {
+ this.init()
+ }
+ },
+ beforeInit() {
+ this.url = 'api/logs/user'
+ return true
+ },
+ cropUploadSuccess(jsonData, field) {
+ store.dispatch('GetInfo').then(() => {})
+ },
+ doSubmit() {
+ if (this.$refs['form']) {
+ this.$refs['form'].validate((valid) => {
+ if (valid) {
+ this.saveLoading = true
+ editUser(this.form).then(() => {
+ this.editSuccessNotify()
+ store.dispatch('GetInfo').then(() => {})
+ this.saveLoading = false
+ }).catch(() => {
+ this.saveLoading = false
+ })
+ }
+ })
+ }
+ }
+ }
+}
+</script>
+
+<style rel="stylesheet/scss" lang="scss">
+ .avatar {
+ width: 120px;
+ height: 120px;
+ border-radius: 50%;
+ }
+ .user-info {
+ padding-left: 0;
+ list-style: none;
+ li{
+ border-bottom: 1px solid #F0F3F4;
+ padding: 11px 0;
+ font-size: 13px;
+ }
+ .user-right {
+ float: right;
+ a{
+ color: #317EF3;
+ }
+ }
+ }
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/system/user/center/updateEmail.vue b/UI source code/dns_mapping_ui-master/src/views/system/user/center/updateEmail.vue
new file mode 100644
index 0000000..b246684
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/system/user/center/updateEmail.vue
@@ -0,0 +1,137 @@
+<template>
+ <div style="display: inline-block;">
+ <el-dialog :visible.sync="dialog" :close-on-click-modal="false" :before-close="cancel" :title="title" append-to-body width="475px" @close="cancel">
+ <el-form ref="form" :model="form" :rules="rules" size="small" label-width="88px">
+ <el-form-item label="新邮箱" prop="email">
+ <el-input v-model="form.email" auto-complete="on" style="width: 200px;" />
+ <el-button :loading="codeLoading" :disabled="isDisabled" size="small" @click="sendCode">{{ buttonName }}</el-button>
+ </el-form-item>
+ <el-form-item label="验证码" prop="code">
+ <el-input v-model="form.code" style="width: 320px;" />
+ </el-form-item>
+ <el-form-item label="当前密码" prop="pass">
+ <el-input v-model="form.pass" type="password" style="width: 320px;" />
+ </el-form-item>
+ </el-form>
+ <div slot="footer" class="dialog-footer">
+ <el-button type="text" @click="cancel">取消</el-button>
+ <el-button :loading="loading" type="primary" @click="doSubmit">确认</el-button>
+ </div>
+ </el-dialog>
+ </div>
+</template>
+
+<script>
+import store from '@/store'
+import { validEmail } from '@/utils/validate'
+import { updateEmail } from '@/api/system/user'
+import { resetEmail } from '@/api/system/code'
+export default {
+ props: {
+ email: {
+ type: String,
+ required: true
+ }
+ },
+ data() {
+ const validMail = (rule, value, callback) => {
+ if (value === '' || value === null) {
+ callback(new Error('新邮箱不能为空'))
+ } else if (value === this.email) {
+ callback(new Error('新邮箱不能与旧邮箱相同'))
+ } else if (validEmail(value)) {
+ callback()
+ } else {
+ callback(new Error('邮箱格式错误'))
+ }
+ }
+ return {
+ loading: false, dialog: false, title: '修改邮箱', form: { pass: '', email: '', code: '' },
+ user: { email: '', password: '' }, codeLoading: false,
+ buttonName: '获取验证码', isDisabled: false, time: 60,
+ rules: {
+ pass: [
+ { required: true, message: '当前密码不能为空', trigger: 'blur' }
+ ],
+ email: [
+ { required: true, validator: validMail, trigger: 'blur' }
+ ],
+ code: [
+ { required: true, message: '验证码不能为空', trigger: 'blur' }
+ ]
+ }
+ }
+ },
+ methods: {
+ cancel() {
+ this.resetForm()
+ },
+ sendCode() {
+ if (this.form.email && this.form.email !== this.email) {
+ this.codeLoading = true
+ this.buttonName = '验证码发送中'
+ const _this = this
+ resetEmail(this.form.email).then(res => {
+ this.$message({
+ showClose: true,
+ message: '发送成功,验证码有效期5分钟',
+ type: 'success'
+ })
+ this.codeLoading = false
+ this.isDisabled = true
+ this.buttonName = this.time-- + '秒后重新发送'
+ this.timer = window.setInterval(function() {
+ _this.buttonName = _this.time + '秒后重新发送'
+ --_this.time
+ if (_this.time < 0) {
+ _this.buttonName = '重新发送'
+ _this.time = 60
+ _this.isDisabled = false
+ window.clearInterval(_this.timer)
+ }
+ }, 1000)
+ }).catch(err => {
+ this.resetForm()
+ this.codeLoading = false
+ console.log(err.response.data.message)
+ })
+ }
+ },
+ doSubmit() {
+ this.$refs['form'].validate((valid) => {
+ if (valid) {
+ this.loading = true
+ updateEmail(this.form).then(res => {
+ this.loading = false
+ this.resetForm()
+ this.$notify({
+ title: '邮箱修改成功',
+ type: 'success',
+ duration: 1500
+ })
+ store.dispatch('GetInfo').then(() => {})
+ }).catch(err => {
+ this.loading = false
+ console.log(err.response.data.message)
+ })
+ } else {
+ return false
+ }
+ })
+ },
+ resetForm() {
+ this.dialog = false
+ this.$refs['form'].resetFields()
+ window.clearInterval(this.timer)
+ this.time = 60
+ this.buttonName = '获取验证码'
+ this.isDisabled = false
+ this.form = { pass: '', email: '', code: '' }
+ }
+ }
+}
+</script>
+
+<style scoped>
+
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/system/user/center/updatePass.vue b/UI source code/dns_mapping_ui-master/src/views/system/user/center/updatePass.vue
new file mode 100644
index 0000000..078ed17
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/system/user/center/updatePass.vue
@@ -0,0 +1,95 @@
+<template>
+ <div style="display: inline-block">
+ <el-dialog :visible.sync="dialog" :close-on-click-modal="false" :before-close="cancel" :title="title" append-to-body width="500px" @close="cancel">
+ <el-form ref="form" :model="form" :rules="rules" size="small" label-width="88px">
+ <el-form-item label="旧密码" prop="oldPass">
+ <el-input v-model="form.oldPass" type="password" auto-complete="on" style="width: 370px;" />
+ </el-form-item>
+ <el-form-item label="新密码" prop="newPass">
+ <el-input v-model="form.newPass" type="password" auto-complete="on" style="width: 370px;" />
+ </el-form-item>
+ <el-form-item label="确认密码" prop="confirmPass">
+ <el-input v-model="form.confirmPass" type="password" auto-complete="on" style="width: 370px;" />
+ </el-form-item>
+ </el-form>
+ <div slot="footer" class="dialog-footer">
+ <el-button type="text" @click="cancel">取消</el-button>
+ <el-button :loading="loading" type="primary" @click="doSubmit">确认</el-button>
+ </div>
+ </el-dialog>
+ </div>
+</template>
+
+<script>
+import store from '@/store'
+import { updatePass } from '@/api/system/user'
+export default {
+ data() {
+ const confirmPass = (rule, value, callback) => {
+ if (value) {
+ if (this.form.newPass !== value) {
+ callback(new Error('两次输入的密码不一致'))
+ } else {
+ callback()
+ }
+ } else {
+ callback(new Error('请再次输入密码'))
+ }
+ }
+ return {
+ loading: false, dialog: false, title: '修改密码', form: { oldPass: '', newPass: '', confirmPass: '' },
+ rules: {
+ oldPass: [
+ { required: true, message: '请输入旧密码', trigger: 'blur' }
+ ],
+ newPass: [
+ { required: true, message: '请输入新密码', trigger: 'blur' },
+ { min: 6, max: 20, message: '长度在 6 到 20 个字符', trigger: 'blur' }
+ ],
+ confirmPass: [
+ { required: true, validator: confirmPass, trigger: 'blur' }
+ ]
+ }
+ }
+ },
+ methods: {
+ cancel() {
+ this.resetForm()
+ },
+ doSubmit() {
+ this.$refs['form'].validate((valid) => {
+ if (valid) {
+ this.loading = true
+ updatePass(this.form).then(res => {
+ this.resetForm()
+ this.$notify({
+ title: '密码修改成功,请重新登录',
+ type: 'success',
+ duration: 1500
+ })
+ setTimeout(() => {
+ store.dispatch('LogOut').then(() => {
+ location.reload() // 为了重新实例化vue-router对象 避免bug
+ })
+ }, 1500)
+ }).catch(err => {
+ this.loading = false
+ console.log(err.response.data.message)
+ })
+ } else {
+ return false
+ }
+ })
+ },
+ resetForm() {
+ this.dialog = false
+ this.$refs['form'].resetFields()
+ this.form = { oldPass: '', newPass: '', confirmPass: '' }
+ }
+ }
+}
+</script>
+
+<style scoped>
+
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/system/user/index.vue b/UI source code/dns_mapping_ui-master/src/views/system/user/index.vue
new file mode 100644
index 0000000..9017069
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/system/user/index.vue
@@ -0,0 +1,484 @@
+<template>
+ <div class="app-container">
+ <el-row :gutter="20">
+ <!--侧边部门数据-->
+ <el-col :xs="9" :sm="6" :md="5" :lg="4" :xl="4">
+ <div class="head-container">
+ <el-input
+ v-model="deptName"
+ clearable
+ size="small"
+ placeholder="输入部门名称搜索"
+ prefix-icon="el-icon-search"
+ class="filter-item"
+ @input="getDeptDatas"
+ />
+ </div>
+ <el-tree
+ :data="deptDatas"
+ :load="getDeptDatas"
+ :props="defaultProps"
+ :expand-on-click-node="false"
+ lazy
+ @node-click="handleNodeClick"
+ />
+ </el-col>
+ <!--用户数据-->
+ <el-col :xs="15" :sm="18" :md="19" :lg="20" :xl="20">
+ <!--工具栏-->
+ <div class="head-container">
+ <div v-if="crud.props.searchToggle">
+ <!-- 搜索 -->
+ <el-input
+ v-model="query.blurry"
+ clearable
+ size="small"
+ placeholder="输入名称或者邮箱搜索"
+ style="width: 200px;"
+ class="filter-item"
+ @keyup.enter.native="crud.toQuery"
+ />
+ <date-range-picker v-model="query.createTime" class="date-item" />
+ <el-select
+ v-model="query.enabled"
+ clearable
+ size="small"
+ placeholder="状态"
+ class="filter-item"
+ style="width: 90px"
+ @change="crud.toQuery"
+ >
+ <el-option
+ v-for="item in enabledTypeOptions"
+ :key="item.key"
+ :label="item.display_name"
+ :value="item.key"
+ />
+ </el-select>
+ <rrOperation />
+ </div>
+ <crudOperation show="" :permission="permission" />
+ </div>
+ <!--表单渲染-->
+ <el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="570px">
+ <el-form ref="form" :inline="true" :model="form" :rules="rules" size="small" label-width="66px">
+ <el-form-item label="用户名" prop="username">
+ <el-input v-model="form.username" @keydown.native="keydown($event)" />
+ </el-form-item>
+ <el-form-item label="电话" prop="phone">
+ <el-input v-model.number="form.phone" />
+ </el-form-item>
+ <el-form-item label="昵称" prop="nickName">
+ <el-input v-model="form.nickName" @keydown.native="keydown($event)" />
+ </el-form-item>
+ <el-form-item label="邮箱" prop="email">
+ <el-input v-model="form.email" />
+ </el-form-item>
+ <el-form-item label="部门" prop="dept.id">
+ <treeselect
+ v-model="form.dept.id"
+ :options="depts"
+ :load-options="loadDepts"
+ style="width: 178px"
+ placeholder="选择部门"
+ />
+ </el-form-item>
+ <el-form-item label="岗位" prop="jobs">
+ <el-select
+ v-model="jobDatas"
+ style="width: 178px"
+ multiple
+ placeholder="请选择"
+ @remove-tag="deleteTag"
+ @change="changeJob"
+ >
+ <el-option
+ v-for="item in jobs"
+ :key="item.name"
+ :label="item.name"
+ :value="item.id"
+ />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="性别">
+ <el-radio-group v-model="form.gender" style="width: 178px">
+ <el-radio label="男">男</el-radio>
+ <el-radio label="女">女</el-radio>
+ </el-radio-group>
+ </el-form-item>
+ <el-form-item label="状态">
+ <el-radio-group v-model="form.enabled" :disabled="form.id === user.id">
+ <el-radio
+ v-for="item in dict.user_status"
+ :key="item.id"
+ :label="item.value"
+ >{{ item.label }}</el-radio>
+ </el-radio-group>
+ </el-form-item>
+ <el-form-item style="margin-bottom: 0;" label="角色" prop="roles">
+ <el-select
+ v-model="roleDatas"
+ style="width: 437px"
+ multiple
+ placeholder="请选择"
+ @remove-tag="deleteTag"
+ @change="changeRole"
+ >
+ <el-option
+ v-for="item in roles"
+ :key="item.name"
+ :disabled="level !== 1 && item.level <= level"
+ :label="item.name"
+ :value="item.id"
+ />
+ </el-select>
+ </el-form-item>
+ </el-form>
+ <div slot="footer" class="dialog-footer">
+ <el-button type="text" @click="crud.cancelCU">取消</el-button>
+ <el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
+ </div>
+ </el-dialog>
+ <!--表格渲染-->
+ <el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
+ <el-table-column :selectable="checkboxT" type="selection" width="55" />
+ <el-table-column :show-overflow-tooltip="true" prop="username" label="用户名" />
+ <el-table-column :show-overflow-tooltip="true" prop="nickName" label="昵称" />
+ <el-table-column prop="gender" label="性别" />
+ <el-table-column :show-overflow-tooltip="true" prop="phone" width="100" label="电话" />
+ <el-table-column :show-overflow-tooltip="true" width="135" prop="email" label="邮箱" />
+ <el-table-column :show-overflow-tooltip="true" prop="dept" label="部门">
+ <template slot-scope="scope">
+ <div>{{ scope.row.dept.name }}</div>
+ </template>
+ </el-table-column>
+ <el-table-column label="状态" align="center" prop="enabled">
+ <template slot-scope="scope">
+ <el-switch
+ v-model="scope.row.enabled"
+ :disabled="user.id === scope.row.id"
+ active-color="#409EFF"
+ inactive-color="#F56C6C"
+ @change="changeEnabled(scope.row, scope.row.enabled)"
+ />
+ </template>
+ </el-table-column>
+ <el-table-column :show-overflow-tooltip="true" prop="createTime" width="135" label="创建日期" />
+ <el-table-column
+ v-if="checkPer(['admin','user:edit','user:del'])"
+ label="操作"
+ width="115"
+ align="center"
+ fixed="right"
+ >
+ <template slot-scope="scope">
+ <udOperation
+ :data="scope.row"
+ :permission="permission"
+ :disabled-dle="scope.row.id === user.id"
+ />
+ </template>
+ </el-table-column>
+ </el-table>
+ <!--分页组件-->
+ <pagination />
+ </el-col>
+ </el-row>
+ </div>
+</template>
+
+<script>
+import crudUser from '@/api/system/user'
+import { isvalidPhone } from '@/utils/validate'
+import { getDepts, getDeptSuperior } from '@/api/system/dept'
+import { getAll, getLevel } from '@/api/system/role'
+import { getAllJob } from '@/api/system/job'
+import CRUD, { presenter, header, form, crud } from '@crud/crud'
+import rrOperation from '@crud/RR.operation'
+import crudOperation from '@crud/CRUD.operation'
+import udOperation from '@crud/UD.operation'
+import pagination from '@crud/Pagination'
+import DateRangePicker from '@/components/DateRangePicker'
+import Treeselect from '@riophae/vue-treeselect'
+import { mapGetters } from 'vuex'
+import '@riophae/vue-treeselect/dist/vue-treeselect.css'
+import { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect'
+let userRoles = []
+let userJobs = []
+const defaultForm = { id: null, username: null, nickName: null, gender: '男', email: null, enabled: 'false', roles: [], jobs: [], dept: { id: null }, phone: null }
+export default {
+ name: 'User',
+ components: { Treeselect, crudOperation, rrOperation, udOperation, pagination, DateRangePicker },
+ cruds() {
+ return CRUD({ title: '用户', url: 'api/users', crudMethod: { ...crudUser }})
+ },
+ mixins: [presenter(), header(), form(defaultForm), crud()],
+ // 数据字典
+ dicts: ['user_status'],
+ data() {
+ // 自定义验证
+ const validPhone = (rule, value, callback) => {
+ if (!value) {
+ callback(new Error('请输入电话号码'))
+ } else if (!isvalidPhone(value)) {
+ callback(new Error('请输入正确的11位手机号码'))
+ } else {
+ callback()
+ }
+ }
+ return {
+ height: document.documentElement.clientHeight - 180 + 'px;',
+ deptName: '', depts: [], deptDatas: [], jobs: [], level: 3, roles: [],
+ jobDatas: [], roleDatas: [], // 多选时使用
+ defaultProps: { children: 'children', label: 'name', isLeaf: 'leaf' },
+ permission: {
+ add: ['admin', 'user:add'],
+ edit: ['admin', 'user:edit'],
+ del: ['admin', 'user:del']
+ },
+ enabledTypeOptions: [
+ { key: 'true', display_name: '激活' },
+ { key: 'false', display_name: '锁定' }
+ ],
+ rules: {
+ username: [
+ { required: true, message: '请输入用户名', trigger: 'blur' },
+ { min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
+ ],
+ nickName: [
+ { required: true, message: '请输入用户昵称', trigger: 'blur' },
+ { min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
+ ],
+ email: [
+ { required: true, message: '请输入邮箱地址', trigger: 'blur' },
+ { type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }
+ ],
+ phone: [
+ { required: true, trigger: 'blur', validator: validPhone }
+ ]
+ }
+ }
+ },
+ computed: {
+ ...mapGetters([
+ 'user'
+ ])
+ },
+ created() {
+ this.crud.msg.add = '新增成功,默认密码:123456'
+ },
+ mounted: function() {
+ const that = this
+ window.onresize = function temp() {
+ that.height = document.documentElement.clientHeight - 180 + 'px;'
+ }
+ },
+ methods: {
+ // 禁止输入空格
+ keydown(e) {
+ if (e.keyCode === 32) {
+ e.returnValue = false
+ }
+ },
+ changeRole(value) {
+ userRoles = []
+ value.forEach(function(data, index) {
+ const role = { id: data }
+ userRoles.push(role)
+ })
+ },
+ changeJob(value) {
+ userJobs = []
+ value.forEach(function(data, index) {
+ const job = { id: data }
+ userJobs.push(job)
+ })
+ },
+ deleteTag(value) {
+ userRoles.forEach(function(data, index) {
+ if (data.id === value) {
+ userRoles.splice(index, value)
+ }
+ })
+ },
+ // 新增与编辑前做的操作
+ [CRUD.HOOK.afterToCU](crud, form) {
+ this.getRoles()
+ if (form.id == null) {
+ this.getDepts()
+ } else {
+ this.getSupDepts(form.dept.id)
+ }
+ this.getRoleLevel()
+ this.getJobs()
+ form.enabled = form.enabled.toString()
+ },
+ // 新增前将多选的值设置为空
+ [CRUD.HOOK.beforeToAdd]() {
+ this.jobDatas = []
+ this.roleDatas = []
+ },
+ // 初始化编辑时候的角色与岗位
+ [CRUD.HOOK.beforeToEdit](crud, form) {
+ this.getJobs(this.form.dept.id)
+ this.jobDatas = []
+ this.roleDatas = []
+ userRoles = []
+ userJobs = []
+ const _this = this
+ form.roles.forEach(function(role, index) {
+ _this.roleDatas.push(role.id)
+ const rol = { id: role.id }
+ userRoles.push(rol)
+ })
+ form.jobs.forEach(function(job, index) {
+ _this.jobDatas.push(job.id)
+ const data = { id: job.id }
+ userJobs.push(data)
+ })
+ },
+ // 提交前做的操作
+ [CRUD.HOOK.afterValidateCU](crud) {
+ if (!crud.form.dept.id) {
+ this.$message({
+ message: '部门不能为空',
+ type: 'warning'
+ })
+ return false
+ } else if (this.jobDatas.length === 0) {
+ this.$message({
+ message: '岗位不能为空',
+ type: 'warning'
+ })
+ return false
+ } else if (this.roleDatas.length === 0) {
+ this.$message({
+ message: '角色不能为空',
+ type: 'warning'
+ })
+ return false
+ }
+ crud.form.roles = userRoles
+ crud.form.jobs = userJobs
+ return true
+ },
+ // 获取左侧部门数据
+ getDeptDatas(node, resolve) {
+ const sort = 'id,desc'
+ const params = { sort: sort }
+ if (typeof node !== 'object') {
+ if (node) {
+ params['name'] = node
+ }
+ } else if (node.level !== 0) {
+ params['pid'] = node.data.id
+ }
+ setTimeout(() => {
+ getDepts(params).then(res => {
+ if (resolve) {
+ resolve(res.content)
+ } else {
+ this.deptDatas = res.content
+ }
+ })
+ }, 100)
+ },
+ getDepts() {
+ getDepts({ enabled: true }).then(res => {
+ this.depts = res.content.map(function(obj) {
+ if (obj.hasChildren) {
+ obj.children = null
+ }
+ return obj
+ })
+ })
+ },
+ getSupDepts(deptId) {
+ getDeptSuperior(deptId).then(res => {
+ const date = res.content
+ this.buildDepts(date)
+ this.depts = date
+ })
+ },
+ buildDepts(depts) {
+ depts.forEach(data => {
+ if (data.children) {
+ this.buildDepts(data.children)
+ }
+ if (data.hasChildren && !data.children) {
+ data.children = null
+ }
+ })
+ },
+ // 获取弹窗内部门数据
+ loadDepts({ action, parentNode, callback }) {
+ if (action === LOAD_CHILDREN_OPTIONS) {
+ getDepts({ enabled: true, pid: parentNode.id }).then(res => {
+ parentNode.children = res.content.map(function(obj) {
+ if (obj.hasChildren) {
+ obj.children = null
+ }
+ return obj
+ })
+ setTimeout(() => {
+ callback()
+ }, 200)
+ })
+ }
+ },
+ // 切换部门
+ handleNodeClick(data) {
+ if (data.pid === 0) {
+ this.query.deptId = null
+ } else {
+ this.query.deptId = data.id
+ }
+ this.crud.toQuery()
+ },
+ // 改变状态
+ changeEnabled(data, val) {
+ this.$confirm('此操作将 "' + this.dict.label.user_status[val] + '" ' + data.username + ', 是否继续?', '提示', {
+ confirmButtonText: '确定',
+ cancelButtonText: '取消',
+ type: 'warning'
+ }).then(() => {
+ crudUser.edit(data).then(res => {
+ this.crud.notify(this.dict.label.user_status[val] + '成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
+ }).catch(() => {
+ data.enabled = !data.enabled
+ })
+ }).catch(() => {
+ data.enabled = !data.enabled
+ })
+ },
+ // 获取弹窗内角色数据
+ getRoles() {
+ getAll().then(res => {
+ this.roles = res
+ }).catch(() => { })
+ },
+ // 获取弹窗内岗位数据
+ getJobs() {
+ getAllJob().then(res => {
+ this.jobs = res.content
+ }).catch(() => { })
+ },
+ // 获取权限级别
+ getRoleLevel() {
+ getLevel().then(res => {
+ this.level = res.level
+ }).catch(() => { })
+ },
+ checkboxT(row, rowIndex) {
+ return row.id !== this.user.id
+ }
+ }
+}
+</script>
+
+<style rel="stylesheet/scss" lang="scss" scoped>
+ ::v-deep .vue-treeselect__control,::v-deep .vue-treeselect__placeholder,::v-deep .vue-treeselect__single-value {
+ height: 30px;
+ line-height: 30px;
+ }
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/tools/aliPay/config.vue b/UI source code/dns_mapping_ui-master/src/views/tools/aliPay/config.vue
new file mode 100644
index 0000000..e7b50d4
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/tools/aliPay/config.vue
@@ -0,0 +1,98 @@
+<template>
+ <el-form ref="form" :model="form" :rules="rules" style="margin-top: 6px;" size="small" label-width="100px">
+ <el-form-item label="appID" prop="appId">
+ <el-input v-model="form.appId" style="width: 40%" />
+ <span style="color: #C0C0C0;margin-left: 10px;">应用APPID,收款账号既是APPID对应支付宝账号</span>
+ </el-form-item>
+ <el-form-item label="商家账号" prop="sysServiceProviderId">
+ <el-input v-model="form.sysServiceProviderId" style="width: 40%;" />
+ <span style="color: #C0C0C0;margin-left: 10px;">商家账号</span>
+ </el-form-item>
+ <el-form-item label="商户私钥" prop="privateKey">
+ <el-input v-model="form.privateKey" type="password" style="width: 40%;" />
+ <span style="color: #C0C0C0;margin-left: 10px;">商户私钥,你的PKCS8格式RSA2私钥</span>
+ </el-form-item>
+ <el-form-item label="支付宝公钥" prop="publicKey">
+ <el-input v-model="form.publicKey" type="password" style="width: 40%;" />
+ <span style="color: #C0C0C0;margin-left: 10px;">支付宝公钥</span>
+ </el-form-item>
+ <el-form-item label="回调地址" prop="returnUrl">
+ <el-input v-model="form.returnUrl" style="width: 40%;" />
+ <span style="color: #C0C0C0;margin-left: 10px;">订单完成后返回的地址</span>
+ </el-form-item>
+ <el-form-item label="异步通知" prop="notifyUrl">
+ <el-input v-model="form.notifyUrl" style="width: 40%;" />
+ <span style="color: #C0C0C0;margin-left: 10px;">支付结果异步通知地址</span>
+ </el-form-item>
+ <el-form-item label="">
+ <el-button :loading="loading" size="medium" type="primary" @click="doSubmit">保存配置</el-button>
+ </el-form-item>
+ </el-form>
+</template>
+
+<script>
+import { get, update } from '@/api/tools/alipay'
+export default {
+ name: 'Config',
+ data() {
+ return {
+ loading: false,
+ form: { appId: '', sysServiceProviderId: '', privateKey: '', publicKey: '', returnUrl: '', notifyUrl: '' },
+ rules: {
+ appId: [
+ { required: true, message: '请输入appID', trigger: 'blur' }
+ ],
+ sysServiceProviderId: [
+ { required: true, message: '请输入商家账号', trigger: 'blur' }
+ ],
+ privateKey: [
+ { required: true, message: '商户私钥不能为空', trigger: 'blur' }
+ ],
+ publicKey: [
+ { required: true, message: '支付宝公钥不能为空', trigger: 'blur' }
+ ],
+ returnUrl: [
+ { required: true, message: '回调地址不能为空', trigger: 'blur' }
+ ],
+ notifyUrl: [
+ { required: true, message: '回调地址不能为空', trigger: 'blur' }
+ ]
+ }
+ }
+ },
+ created() {
+ this.init()
+ },
+ methods: {
+ init() {
+ get().then(res => {
+ this.form = res
+ })
+ },
+ doSubmit() {
+ this.$refs['form'].validate((valid) => {
+ if (valid) {
+ this.loading = true
+ update(this.form).then(res => {
+ this.$notify({
+ title: '修改成功',
+ type: 'success',
+ duration: 2500
+ })
+ this.loading = false
+ }).catch(err => {
+ this.loading = false
+ console.log(err.response.data.message)
+ })
+ } else {
+ return false
+ }
+ })
+ }
+ }
+}
+</script>
+
+<style scoped>
+
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/tools/aliPay/index.vue b/UI source code/dns_mapping_ui-master/src/views/tools/aliPay/index.vue
new file mode 100644
index 0000000..5234929
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/tools/aliPay/index.vue
@@ -0,0 +1,48 @@
+<template>
+ <el-tabs v-model="activeName" style="padding-left: 5px;">
+ <el-tab-pane label="参数配置" name="first">
+ <Config />
+ </el-tab-pane>
+ <el-tab-pane label="支付测试" name="second">
+ <ToPay />
+ </el-tab-pane>
+ <el-tab-pane label="使用说明" name="third">
+ <div>
+ <blockquote class="my-blockquote">注意</blockquote>
+ <pre class="my-code">
+测试所用参数都是沙箱环境,仅供测试使用,申请地址:<a style="color: #00a0e9" href="https://openhome.alipay.com/platform/appDaily.htm?tab=info" target="_blank">支付宝开发平台</a>
+如需付款测试,请使用
+密码与支付密码:111111</pre>
+ <blockquote class="my-blockquote"> 支付设置</blockquote>
+ <pre class="my-code">
+// 支付提供两个接口,
+// PC端与手机端,并且在前端使用代码识别
+if (/(Android)/i.test(navigator.userAgent)){ // 判断是否为Android手机
+ url = "/aliPay/toPayAsWeb"
+}else if(/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)){ // 判断是否为苹果手机
+ url = "/aliPay/toPayAsWeb"
+} else {
+ url = "/aliPay/toPayAsPC"
+}</pre>
+ </div>
+ </el-tab-pane>
+ </el-tabs>
+</template>
+
+<script>
+import Config from './config'
+import ToPay from './toPay'
+export default {
+ name: 'AliPay',
+ components: { Config, ToPay },
+ data() {
+ return {
+ activeName: 'second'
+ }
+ }
+}
+</script>
+
+<style scoped>
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/tools/aliPay/toPay.vue b/UI source code/dns_mapping_ui-master/src/views/tools/aliPay/toPay.vue
new file mode 100644
index 0000000..595cf08
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/tools/aliPay/toPay.vue
@@ -0,0 +1,86 @@
+<template>
+ <div>
+ <el-form ref="form" :model="form" :rules="rules" style="margin-top: 6px;" size="small" label-width="90px">
+ <el-form-item label="商品名称" prop="subject">
+ <el-input v-model="form.subject" style="width: 35%" />
+ </el-form-item>
+ <el-form-item label="商品价格" prop="totalAmount">
+ <el-input v-model="form.totalAmount" style="width: 35%" />
+ <span style="color: #C0C0C0;margin-left: 10px;">测试允许区间(0,5000]</span>
+ </el-form-item>
+ <el-form-item label="商品描述" prop="body">
+ <el-input v-model="form.body" style="width: 35%" rows="8" type="textarea" />
+ </el-form-item>
+ <el-form-item label="">
+ <el-button :loading="loading" size="medium" type="primary" @click="doSubmit">去支付</el-button>
+ </el-form-item>
+ </el-form>
+ </div>
+</template>
+
+<script>
+import { toAliPay } from '@/api/tools/alipay'
+export default {
+ data() {
+ return {
+ url: '',
+ // 新窗口的引用
+ newWin: null,
+ loading: false, form: { subject: '', totalAmount: '', body: '' },
+ rules: {
+ subject: [
+ { required: true, message: '商品名称不能为空', trigger: 'blur' }
+ ],
+ totalAmount: [
+ { required: true, message: '商品价格不能为空', trigger: 'blur' }
+ ],
+ body: [
+ { required: true, message: '商品描述不能为空', trigger: 'blur' }
+ ]
+ }
+ }
+ },
+ watch: {
+ url(newVal, oldVal) {
+ if (newVal && this.newWin) {
+ this.newWin.sessionStorage.clear()
+ this.newWin.location.href = newVal
+ // 重定向后把url和newWin重置
+ this.url = ''
+ this.newWin = null
+ }
+ }
+ },
+ methods: {
+ doSubmit() {
+ this.$refs['form'].validate((valid) => {
+ if (valid) {
+ this.loading = true
+ // 先打开一个空的新窗口,再请求
+ this.newWin = window.open()
+ let url = ''
+ if (/(Android)/i.test(navigator.userAgent)) { // 判断是否为Android手机
+ url = 'aliPay/toPayAsWeb'
+ } else if (/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)) { // 判断是否为苹果手机
+ url = 'aliPay/toPayAsWeb'
+ } else {
+ url = 'aliPay/toPayAsPC'
+ }
+ toAliPay(url, this.form).then(res => {
+ this.loading = false
+ this.url = res
+ }).catch(err => {
+ this.loading = false
+ console.log(err.response.data.message)
+ })
+ } else {
+ return false
+ }
+ })
+ }
+ }
+}
+</script>
+
+<style scoped>
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/tools/email/config.vue b/UI source code/dns_mapping_ui-master/src/views/tools/email/config.vue
new file mode 100644
index 0000000..b72b724
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/tools/email/config.vue
@@ -0,0 +1,91 @@
+<template>
+ <el-form ref="form" :model="form" :rules="rules" style="margin-top: 6px;" size="small" label-width="100px">
+ <el-form-item label="发件人邮箱" prop="fromUser">
+ <el-input v-model="form.fromUser" style="width: 40%" />
+ <span style="color: #C0C0C0;margin-left: 10px;">Sender mailbox</span>
+ </el-form-item>
+ <el-form-item label="发件用户名" prop="user">
+ <el-input v-model="form.user" style="width: 40%;" />
+ <span style="color: #C0C0C0;margin-left: 10px;">Sender usernamex</span>
+ </el-form-item>
+ <el-form-item label="邮箱密码" prop="pass">
+ <el-input v-model="form.pass" type="password" style="width: 40%;" />
+ <span style="color: #C0C0C0;margin-left: 10px;">email Password</span>
+ </el-form-item>
+ <el-form-item label="SMTP地址" prop="host">
+ <el-input v-model="form.host" style="width: 40%;" />
+ <span style="color: #C0C0C0;margin-left: 10px;">SMTP address</span>
+ </el-form-item>
+ <el-form-item label="SMTP端口" prop="port">
+ <el-input v-model="form.port" style="width: 40%;" />
+ <span style="color: #C0C0C0;margin-left: 10px;">SMTP port</span>
+ </el-form-item>
+ <el-form-item label="">
+ <el-button :loading="loading" size="medium" type="primary" @click="doSubmit">保存配置</el-button>
+ </el-form-item>
+ </el-form>
+</template>
+
+<script>
+import { get, update } from '@/api/tools/email'
+export default {
+ name: 'Config',
+ data() {
+ return {
+ loading: false, form: { id: 1, fromUser: '', user: '', pass: '', host: '', port: '', sslEnable: '' },
+ rules: {
+ fromUser: [
+ { required: true, message: '请输入发件人邮箱', trigger: 'blur' },
+ { type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }
+ ],
+ user: [
+ { required: true, message: '请输入发件用户名', trigger: 'blur' }
+ ],
+ pass: [
+ { required: true, message: '密码不能为空', trigger: 'blur' }
+ ],
+ host: [
+ { required: true, message: 'SMTP地址不能为空', trigger: 'blur' }
+ ],
+ port: [
+ { required: true, message: 'SMTP端口不能为空', trigger: 'blur' }
+ ]
+ }
+ }
+ },
+ created() {
+ this.init()
+ },
+ methods: {
+ init() {
+ get().then(res => {
+ this.form = res
+ })
+ },
+ doSubmit() {
+ this.$refs['form'].validate((valid) => {
+ if (valid) {
+ this.loading = true
+ update(this.form).then(res => {
+ this.$notify({
+ title: '修改成功',
+ type: 'success',
+ duration: 2500
+ })
+ this.loading = false
+ }).catch(err => {
+ this.loading = false
+ console.log(err.response.data.message)
+ })
+ } else {
+ return false
+ }
+ })
+ }
+ }
+}
+</script>
+
+<style scoped>
+
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/tools/email/index.vue b/UI source code/dns_mapping_ui-master/src/views/tools/email/index.vue
new file mode 100644
index 0000000..d0e6387
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/tools/email/index.vue
@@ -0,0 +1,41 @@
+<template>
+ <el-tabs v-model="activeName" style="padding-left: 8px;">
+ <el-tab-pane label="邮箱配置" name="first">
+ <Config />
+ </el-tab-pane>
+ <el-tab-pane label="发送邮件" name="second">
+ <Send />
+ </el-tab-pane>
+ <el-tab-pane label="使用说明" name="third">
+ <div>
+ <blockquote class="my-blockquote"> 邮件服务器配置</blockquote>
+ <pre class="my-code">
+ # 邮件服务器的SMTP地址,可选,默认为smtp
+ # 邮件服务器的SMTP端口,可选,默认465或者25
+ # 发件人(必须正确,否则发送失败)
+ # 用户名,默认为发件人邮箱前缀
+ # 密码(注意,某些邮箱需要为SMTP服务单独设置密码,如QQ和163等等)
+ # 是否开启ssl,默认开启</pre>
+ <blockquote class="my-blockquote">更多帮助</blockquote>
+ <pre class="my-code">更多帮助请查看文档:<a style="color:#009688" href="http://hutool.mydoc.io/#text_319499" target="_black">hutool工具包</a></pre>
+ </div>
+ </el-tab-pane>
+ </el-tabs>
+</template>
+
+<script>
+import Config from './config'
+import Send from './send'
+export default {
+ name: 'Email',
+ components: { Config, Send },
+ data() {
+ return {
+ activeName: 'second'
+ }
+ }
+}
+</script>
+
+<style scoped>
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/tools/email/send.vue b/UI source code/dns_mapping_ui-master/src/views/tools/email/send.vue
new file mode 100644
index 0000000..d4bd568
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/tools/email/send.vue
@@ -0,0 +1,98 @@
+<template>
+ <div>
+ <el-form ref="form" :model="form" :rules="rules" style="margin-top: 6px;" size="small" label-width="100px">
+ <el-form-item label="邮件标题" prop="subject">
+ <el-input v-model="form.subject" style="width: 646px" placeholder="请输入邮件标题,标题不能为空" />
+ </el-form-item>
+ <el-form-item label="收件地址" prop="tos">
+ <el-input v-model="form.tos" style="width: 646px" placeholder="请输入收件地址,多个地址英文逗号,隔开" />
+ </el-form-item>
+ <div ref="editor" class="editor" />
+ <el-button :loading="loading" style="margin-left:1.6%;margin-bottom: 30px" size="medium" type="primary" @click="doSubmit">发送邮件</el-button>
+ </el-form>
+ </div>
+</template>
+
+<script>
+import { send } from '@/api/tools/email'
+import { upload } from '@/utils/upload'
+import { mapGetters } from 'vuex'
+import E from 'wangeditor'
+export default {
+ name: 'Index',
+ data() {
+ return {
+ loading: false, form: { subject: '', tos: '', content: '' },
+ rules: {
+ subject: [
+ { required: true, message: '标题不能为空', trigger: 'blur' }
+ ],
+ tos: [
+ { required: true, message: '收件人不能为空', trigger: 'blur' }
+ ]
+ }
+ }
+ },
+ computed: {
+ ...mapGetters([
+ 'imagesUploadApi',
+ 'baseApi'
+ ])
+ },
+ mounted() {
+ const _this = this
+ var editor = new E(this.$refs.editor)
+ // 自定义菜单配置
+ editor.config.zIndex = 10
+ // 文件上传
+ editor.config.customUploadImg = function(files, insert) {
+ // files 是 input 中选中的文件列表
+ // insert 是获取图片 url 后,插入到编辑器的方法
+ files.forEach(image => {
+ upload(_this.imagesUploadApi, image).then(res => {
+ const data = res.data
+ const url = _this.baseApi + '/file/' + data.type + '/' + data.realName
+ insert(url)
+ })
+ })
+ }
+ editor.config.onchange = (html) => {
+ this.form.content = html
+ }
+ editor.create()
+ },
+ methods: {
+ doSubmit() {
+ this.$refs['form'].validate((valid) => {
+ if (valid) {
+ this.loading = true
+ send(this.form).then(res => {
+ this.$notify({
+ title: '发送成功',
+ type: 'success',
+ duration: 2500
+ })
+ this.loading = false
+ }).catch(err => {
+ this.loading = false
+ console.log(err.response.data.message)
+ })
+ } else {
+ return false
+ }
+ })
+ }
+ }
+}
+</script>
+
+<style scoped>
+ .editor{
+ text-align:left;
+ margin: 20px;
+ width: 730px;
+ }
+ ::v-deep .w-e-text-container {
+ height: 360px !important;
+ }
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/tools/storage/index.vue b/UI source code/dns_mapping_ui-master/src/views/tools/storage/index.vue
new file mode 100644
index 0000000..5bb3fc0
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/tools/storage/index.vue
@@ -0,0 +1,36 @@
+<template>
+ <el-tabs v-model="activeName" style="padding-left: 8px;" @tab-click="tabClick">
+ <el-tab-pane label="本地存储" name="first">
+ <Local ref="local" />
+ </el-tab-pane>
+ <el-tab-pane label="七牛云存储" name="second">
+ <QiNiu ref="qiNiu" />
+ </el-tab-pane>
+ </el-tabs>
+</template>
+
+<script>
+import QiNiu from './qiniu/index'
+import Local from './local/index'
+export default {
+ name: 'Storage',
+ components: { QiNiu, Local },
+ data() {
+ return {
+ activeName: 'first'
+ }
+ },
+ methods: {
+ tabClick(name) {
+ if (this.activeName === 'first') {
+ this.$refs.local.crud.toQuery()
+ } else {
+ this.$refs.qiNiu.crud.toQuery()
+ }
+ }
+ }
+}
+</script>
+
+<style scoped>
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/tools/storage/local/index.vue b/UI source code/dns_mapping_ui-master/src/views/tools/storage/local/index.vue
new file mode 100644
index 0000000..3adf8a0
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/tools/storage/local/index.vue
@@ -0,0 +1,184 @@
+<template>
+ <div class="app-container" style="padding: 8px;">
+ <!--工具栏-->
+ <div class="head-container">
+ <div v-if="crud.props.searchToggle">
+ <!-- 搜索 -->
+ <el-input v-model="query.blurry" clearable size="small" placeholder="输入内容模糊搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
+ <date-range-picker v-model="query.createTime" class="date-item" />
+ <rrOperation />
+ </div>
+ <crudOperation :permission="permission">
+ <!-- 新增 -->
+ <el-button
+ slot="left"
+ v-permission="['admin','storage:add']"
+ class="filter-item"
+ size="mini"
+ type="primary"
+ icon="el-icon-upload"
+ @click="crud.toAdd"
+ >上传
+ </el-button>
+ </crudOperation>
+ </div>
+ <!--表单组件-->
+ <el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.add ? '文件上传' : '编辑文件'" width="500px">
+ <el-form ref="form" :model="form" size="small" label-width="80px">
+ <el-form-item label="文件名">
+ <el-input v-model="form.name" style="width: 370px;" />
+ </el-form-item>
+ <!-- 上传文件 -->
+ <el-form-item v-if="crud.status.add" label="上传">
+ <el-upload
+ ref="upload"
+ :limit="1"
+ :before-upload="beforeUpload"
+ :auto-upload="false"
+ :headers="headers"
+ :on-success="handleSuccess"
+ :on-error="handleError"
+ :action="fileUploadApi + '?name=' + form.name"
+ >
+ <div class="eladmin-upload"><i class="el-icon-upload" /> 添加文件</div>
+ <div slot="tip" class="el-upload__tip">可上传任意格式文件,且不超过100M</div>
+ </el-upload>
+ </el-form-item>
+ </el-form>
+ <div slot="footer" class="dialog-footer">
+ <el-button type="text" @click="crud.cancelCU">取消</el-button>
+ <el-button v-if="crud.status.add" :loading="loading" type="primary" @click="upload">确认</el-button>
+ <el-button v-else :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
+ </div>
+ </el-dialog>
+ <!--表格渲染-->
+ <el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
+ <el-table-column type="selection" width="55" />
+ <el-table-column prop="name" label="文件名">
+ <template slot-scope="scope">
+ <el-popover
+ :content="'file/' + scope.row.type + '/' + scope.row.realName"
+ placement="top-start"
+ title="路径"
+ width="200"
+ trigger="hover"
+ >
+ <a
+ slot="reference"
+ :href="baseApi + '/file/' + scope.row.type + '/' + scope.row.realName"
+ class="el-link--primary"
+ style="word-break:keep-all;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color: #1890ff;font-size: 13px;"
+ target="_blank"
+ >
+ {{ scope.row.name }}
+ </a>
+ </el-popover>
+ </template>
+ </el-table-column>
+ <el-table-column prop="path" label="预览图">
+ <template slot-scope="{row}">
+ <el-image
+ :src=" baseApi + '/file/' + row.type + '/' + row.realName"
+ :preview-src-list="[baseApi + '/file/' + row.type + '/' + row.realName]"
+ fit="contain"
+ lazy
+ class="el-avatar"
+ >
+ <div slot="error">
+ <i class="el-icon-document" />
+ </div>
+ </el-image>
+ </template>
+ </el-table-column>
+ <el-table-column prop="suffix" label="文件类型" />
+ <el-table-column prop="type" label="类别" />
+ <el-table-column prop="size" label="大小" />
+ <el-table-column prop="operate" label="操作人" />
+ <el-table-column prop="createTime" label="创建日期" />
+ </el-table>
+ <!--分页组件-->
+ <pagination />
+ </div>
+</template>
+
+<script>
+import { mapGetters } from 'vuex'
+import { getToken } from '@/utils/auth'
+import crudFile from '@/api/tools/localStorage'
+import CRUD, { presenter, header, form, crud } from '@crud/crud'
+import rrOperation from '@crud/RR.operation'
+import crudOperation from '@crud/CRUD.operation'
+import pagination from '@crud/Pagination'
+import DateRangePicker from '@/components/DateRangePicker'
+
+const defaultForm = { id: null, name: '' }
+export default {
+ components: { pagination, crudOperation, rrOperation, DateRangePicker },
+ cruds() {
+ return CRUD({ title: '文件', url: 'api/localStorage', crudMethod: { ...crudFile }})
+ },
+ mixins: [presenter(), header(), form(defaultForm), crud()],
+ data() {
+ return {
+ delAllLoading: false,
+ loading: false,
+ headers: { 'Authorization': getToken() },
+ permission: {
+ edit: ['admin', 'storage:edit'],
+ del: ['admin', 'storage:del']
+ }
+ }
+ },
+ computed: {
+ ...mapGetters([
+ 'baseApi',
+ 'fileUploadApi'
+ ])
+ },
+ created() {
+ this.crud.optShow.add = false
+ },
+ methods: {
+ // 上传文件
+ upload() {
+ this.$refs.upload.submit()
+ },
+ beforeUpload(file) {
+ let isLt2M = true
+ isLt2M = file.size / 1024 / 1024 < 100
+ if (!isLt2M) {
+ this.loading = false
+ this.$message.error('上传文件大小不能超过 100MB!')
+ }
+ this.form.name = file.name
+ return isLt2M
+ },
+ handleSuccess(response, file, fileList) {
+ this.crud.notify('上传成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
+ this.$refs.upload.clearFiles()
+ this.crud.status.add = CRUD.STATUS.NORMAL
+ this.crud.resetForm()
+ this.crud.toQuery()
+ },
+ // 监听上传失败
+ handleError(e, file, fileList) {
+ const msg = JSON.parse(e.message)
+ this.$notify({
+ title: msg.message,
+ type: 'error',
+ duration: 2500
+ })
+ this.loading = false
+ }
+ }
+}
+</script>
+
+<style scoped>
+ ::v-deep .el-image__error, .el-image__placeholder{
+ background: none;
+ }
+ ::v-deep .el-image-viewer__wrapper{
+ top: 55px;
+ }
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/tools/storage/qiniu/form.vue b/UI source code/dns_mapping_ui-master/src/views/tools/storage/qiniu/form.vue
new file mode 100644
index 0000000..c77904e
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/tools/storage/qiniu/form.vue
@@ -0,0 +1,98 @@
+<template>
+ <el-dialog :visible.sync="dialog" :close-on-click-modal="false" title="七牛云配置" append-to-body width="580px">
+ <el-form ref="form" :model="form" :rules="rules" style="margin-top: 6px;" size="small" label-width="110px">
+ <el-form-item label="Access Key" prop="accessKey">
+ <el-input v-model="form.accessKey" style="width: 95%" placeholder="accessKey,在安全中心,秘钥管理中查看" />
+ </el-form-item>
+ <el-form-item label="Secret Key" prop="secretKey">
+ <el-input v-model="form.secretKey" type="password" style="width: 95%;" placeholder="secretKey,在安全中心,秘钥管理中查看" />
+ </el-form-item>
+ <el-form-item label="空间名称" prop="bucket">
+ <el-input v-model="form.bucket" style="width: 95%;" placeholder="存储空间名称作为唯一的 Bucket 识别符" />
+ </el-form-item>
+ <el-form-item label="外链域名" prop="host">
+ <el-input v-model="form.host" style="width: 95%;" placeholder="外链域名,可自定义,需在七牛云绑定" />
+ </el-form-item>
+ <el-form-item label="存储区域">
+ <el-select v-model="form.zone" placeholder="请选择存储区域">
+ <el-option
+ v-for="item in zones"
+ :key="item"
+ :label="item"
+ :value="item"
+ />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="空间类型" prop="type">
+ <el-radio v-model="form.type" label="公开">公开</el-radio>
+ <el-radio v-model="form.type" label="私有">私有</el-radio>
+ </el-form-item>
+ </el-form>
+ <div slot="footer" class="dialog-footer">
+ <el-button type="text" @click="dialog = false">取消</el-button>
+ <el-button :loading="loading" type="primary" @click="doSubmit">确认</el-button>
+ </div>
+ </el-dialog>
+</template>
+
+<script>
+import { get, update } from '@/api/tools/qiniu'
+export default {
+ data() {
+ return {
+ zones: ['华东', '华北', '华南', '北美', '东南亚'], dialog: false,
+ loading: false, form: { accessKey: '', secretKey: '', bucket: '', host: '', zone: '', type: '' },
+ rules: {
+ accessKey: [
+ { required: true, message: '请输入accessKey', trigger: 'blur' }
+ ],
+ secretKey: [
+ { required: true, message: '请输入secretKey', trigger: 'blur' }
+ ],
+ bucket: [
+ { required: true, message: '请输入空间名称', trigger: 'blur' }
+ ],
+ host: [
+ { required: true, message: '请输入外链域名', trigger: 'blur' }
+ ],
+ type: [
+ { required: true, message: '空间类型不能为空', trigger: 'blur' }
+ ]
+ }
+ }
+ },
+ methods: {
+ init() {
+ get().then(res => {
+ this.form = res
+ })
+ },
+ doSubmit() {
+ this.$refs['form'].validate((valid) => {
+ if (valid) {
+ this.loading = true
+ update(this.form).then(res => {
+ this.$notify({
+ title: '修改成功',
+ type: 'success',
+ duration: 2500
+ })
+ this.$parent.crud.toQuery()
+ this.loading = false
+ this.dialog = false
+ }).catch(err => {
+ this.loading = false
+ console.log(err.response.data.message)
+ })
+ } else {
+ return false
+ }
+ })
+ }
+ }
+}
+</script>
+
+<style scoped>
+
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/tools/storage/qiniu/index.vue b/UI source code/dns_mapping_ui-master/src/views/tools/storage/qiniu/index.vue
new file mode 100644
index 0000000..bdb9c8d
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/tools/storage/qiniu/index.vue
@@ -0,0 +1,189 @@
+<template>
+ <div class="app-container" style="padding: 8px;">
+ <!--表单组件-->
+ <eForm ref="form" />
+ <!-- 工具栏 -->
+ <div class="head-container">
+ <div v-if="crud.props.searchToggle">
+ <!-- 搜索 -->
+ <el-input v-model="query.key" clearable size="small" placeholder="输入文件名称搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="toQuery" />
+ <date-range-picker v-model="query.createTime" class="date-item" />
+ <rrOperation />
+ </div>
+ <crudOperation :permission="permission">
+ <template slot="left">
+ <!-- 上传 -->
+ <el-button class="filter-item" size="mini" type="primary" icon="el-icon-upload" @click="dialog = true">上传</el-button>
+ <!-- 同步 -->
+ <el-button :icon="icon" class="filter-item" size="mini" type="warning" @click="synchronize">同步</el-button>
+ <!-- 配置 -->
+ <el-button
+ class="filter-item"
+ size="mini"
+ type="success"
+ icon="el-icon-s-tools"
+ @click="doConfig"
+ >配置</el-button>
+ </template>
+ </crudOperation>
+ <!-- 文件上传 -->
+ <el-dialog :visible.sync="dialog" :close-on-click-modal="false" append-to-body width="500px" @close="doSubmit">
+ <el-upload
+ :before-remove="handleBeforeRemove"
+ :on-success="handleSuccess"
+ :on-error="handleError"
+ :file-list="fileList"
+ :headers="headers"
+ :action="qiNiuUploadApi"
+ class="upload-demo"
+ multiple
+ >
+ <el-button size="small" type="primary">点击上传</el-button>
+ <div slot="tip" style="display: block;" class="el-upload__tip">请勿上传违法文件,且文件不超过15M</div>
+ </el-upload>
+ <div slot="footer" class="dialog-footer">
+ <el-button type="primary" @click="doSubmit">确认</el-button>
+ </div>
+ </el-dialog>
+ <!--表格渲染-->
+ <el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
+ <el-table-column type="selection" width="55" />
+ <el-table-column prop="name" :show-overflow-tooltip="true" label="文件名">
+ <template slot-scope="scope">
+ <a href="JavaScript:" class="el-link el-link--primary" target="_blank" type="primary" @click="download(scope.row.id)">{{ scope.row.key }}</a>
+ </template>
+ </el-table-column>
+ <el-table-column :show-overflow-tooltip="true" prop="suffix" label="文件类型" @selection-change="crud.selectionChangeHandler" />
+ <el-table-column prop="bucket" label="空间名称" />
+ <el-table-column prop="size" label="文件大小" />
+ <el-table-column prop="type" label="空间类型" />
+ <el-table-column prop="updateTime" label="创建日期" />
+ </el-table>
+ <!--分页组件-->
+ <pagination />
+ </div>
+ </div>
+</template>
+
+<script>
+import crudQiNiu from '@/api/tools/qiniu'
+import { mapGetters } from 'vuex'
+import { getToken } from '@/utils/auth'
+import eForm from './form'
+import CRUD, { presenter, header, crud } from '@crud/crud'
+import rrOperation from '@crud/RR.operation'
+import crudOperation from '@crud/CRUD.operation'
+import pagination from '@crud/Pagination'
+import DateRangePicker from '@/components/DateRangePicker'
+
+export default {
+ components: { eForm, pagination, crudOperation, rrOperation, DateRangePicker },
+ cruds() {
+ return CRUD({ title: '七牛云文件', url: 'api/qiNiuContent', crudMethod: { ...crudQiNiu }})
+ },
+ mixins: [presenter(), header(), crud()],
+ data() {
+ return {
+ permission: {
+ del: ['admin', 'storage:del']
+ },
+ title: '文件', dialog: false,
+ icon: 'el-icon-refresh',
+ url: '', headers: { 'Authorization': getToken() },
+ dialogImageUrl: '', dialogVisible: false, fileList: [], files: [], newWin: null
+ }
+ },
+ computed: {
+ ...mapGetters([
+ 'qiNiuUploadApi'
+ ])
+ },
+ watch: {
+ url(newVal, oldVal) {
+ if (newVal && this.newWin) {
+ this.newWin.sessionStorage.clear()
+ this.newWin.location.href = newVal
+ // 重定向后把url和newWin重置
+ this.url = ''
+ this.newWin = null
+ }
+ }
+ },
+ created() {
+ this.crud.optShow.add = false
+ this.crud.optShow.edit = false
+ },
+ methods: {
+ // 七牛云配置
+ doConfig() {
+ const _this = this.$refs.form
+ _this.init()
+ _this.dialog = true
+ },
+ handleSuccess(response, file, fileList) {
+ const uid = file.uid
+ const id = response.id
+ this.files.push({ uid, id })
+ },
+ handleBeforeRemove(file, fileList) {
+ for (let i = 0; i < this.files.length; i++) {
+ if (this.files[i].uid === file.uid) {
+ crudQiNiu.del([this.files[i].id]).then(res => {})
+ return true
+ }
+ }
+ },
+ handlePictureCardPreview(file) {
+ this.dialogImageUrl = file.url
+ this.dialogVisible = true
+ },
+ // 刷新列表数据
+ doSubmit() {
+ this.fileList = []
+ this.dialogVisible = false
+ this.dialogImageUrl = ''
+ this.dialog = false
+ this.crud.toQuery()
+ },
+ // 监听上传失败
+ handleError(e, file, fileList) {
+ const msg = JSON.parse(e.message)
+ this.crud.notify(msg.message, CRUD.NOTIFICATION_TYPE.ERROR)
+ },
+ // 下载文件
+ download(id) {
+ this.downloadLoading = true
+ // 先打开一个空的新窗口,再请求
+ this.newWin = window.open()
+ crudQiNiu.download(id).then(res => {
+ this.downloadLoading = false
+ this.url = res.url
+ }).catch(err => {
+ this.downloadLoading = false
+ console.log(err.response.data.message)
+ })
+ },
+ // 同步数据
+ synchronize() {
+ this.icon = 'el-icon-loading'
+ crudQiNiu.sync().then(res => {
+ this.icon = 'el-icon-refresh'
+ this.$message({
+ showClose: true,
+ message: '数据同步成功',
+ type: 'success',
+ duration: 1500
+ })
+ this.crud.toQuery()
+ }).catch(err => {
+ this.icon = 'el-icon-refresh'
+ console.log(err.response.data.message)
+ })
+ }
+ }
+}
+</script>
+
+<style scoped>
+
+</style>
diff --git a/UI source code/dns_mapping_ui-master/src/views/tools/swagger/index.vue b/UI source code/dns_mapping_ui-master/src/views/tools/swagger/index.vue
new file mode 100644
index 0000000..5162cd9
--- /dev/null
+++ b/UI source code/dns_mapping_ui-master/src/views/tools/swagger/index.vue
@@ -0,0 +1,16 @@
+<template>
+ <elFrame :src="swaggerApi" />
+</template>
+<script>
+import { mapGetters } from 'vuex'
+import elFrame from '@/components/Iframe/index'
+export default {
+ name: 'Swagger',
+ components: { elFrame },
+ computed: {
+ ...mapGetters([
+ 'swaggerApi'
+ ])
+ }
+}
+</script>