Browse Source

b端国际化

master
hx 3 days ago
parent
commit
31ec693a16
  1. 103
      src/assets/styles/ruoyi.scss
  2. 56
      src/assets/styles/sidebar.scss
  3. 1617
      src/components/device/TrajectoryDialog.vue
  4. 70
      src/components/device/index.vue
  5. 1004
      src/components/user/index.vue
  6. 4
      src/layout/components/Navbar.vue
  7. 16
      src/layout/components/Sidebar/Item.vue
  8. 54
      src/layout/index.vue
  9. 20
      src/main.js
  10. 2
      src/settings.js
  11. 370
      src/views/device/device/index.vue
  12. 967
      src/views/system/role/index.vue
  13. 100
      src/views/system/user/components/ProfileSettingsCard.vue
  14. 881
      src/views/system/user/index.vue

103
src/assets/styles/ruoyi.scss

@ -130,6 +130,109 @@
border-radius: 4px;
}
.page-query-form {
display: flex;
flex-wrap: wrap;
align-items: flex-end;
gap: 12px 16px;
margin-bottom: 16px;
}
.page-query-form .el-form-item {
width: min(100%, 260px);
margin-right: 0;
margin-bottom: 0;
}
.page-query-form .el-form-item__label {
width: 100% !important;
float: none;
line-height: 20px;
padding: 0 0 6px;
white-space: normal;
word-break: break-word;
}
.page-query-form .el-form-item__content {
width: 100%;
margin-left: 0 !important;
}
.page-query-form .page-query-actions {
width: auto;
min-width: 170px;
}
.page-toolbar {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px 0;
}
.page-toolbar .el-col {
width: auto !important;
max-width: none;
flex: 0 0 auto;
}
.page-toolbar .el-button {
white-space: nowrap;
}
.page-dialog-form .el-form-item__label {
white-space: normal;
line-height: 20px;
}
.page-dialog-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 0 16px;
}
.page-dialog-grid--single {
grid-template-columns: minmax(0, 1fr);
}
.page-dialog-grid__full {
grid-column: 1 / -1;
}
.page-tree-option-group {
display: flex;
flex-wrap: wrap;
gap: 8px 16px;
margin-bottom: 8px;
}
.page-footer-meta {
float: left;
line-height: 32px;
color: #606266;
}
.page-summary-list {
display: flex;
flex-wrap: wrap;
gap: 12px 24px;
margin-bottom: 12px;
color: #606266;
}
.page-inline-filter {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 12px;
margin-bottom: 12px;
}
.page-inline-filter__label {
color: #606266;
font-size: 13px;
}
@media (max-width: 768px) {
.pagination-container .el-pagination > .el-pagination__jump {
display: none !important;

56
src/assets/styles/sidebar.scss

@ -3,7 +3,7 @@
.main-container {
height: 100%;
transition: margin-left .28s;
margin-left: $base-sidebar-width;
margin-left: var(--sidebar-expanded-width, #{$base-sidebar-width});
position: relative;
}
@ -14,7 +14,7 @@
.sidebar-container {
-webkit-transition: width .28s;
transition: width 0.28s;
width: $base-sidebar-width !important;
width: var(--sidebar-expanded-width, #{$base-sidebar-width}) !important;
background-color: $base-menu-background;
height: 100%;
position: fixed;
@ -60,10 +60,6 @@
overflow: hidden;
}
.svg-icon {
margin-right: 16px;
}
.el-menu {
border: none;
height: 100%;
@ -71,11 +67,43 @@
}
.el-menu-item, .el-submenu__title {
display: flex;
align-items: center;
overflow: hidden !important;
text-overflow: ellipsis !important;
white-space: nowrap !important;
}
.menu-item-icon {
flex: 0 0 18px;
display: inline-flex;
align-items: center;
justify-content: center;
width: 18px;
margin-right: 12px;
overflow: hidden;
.svg-icon {
margin-right: 0;
}
}
.menu-item-text {
flex: 1 1 auto;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
line-height: inherit;
}
.el-submenu__icon-arrow {
position: static;
margin-top: 0;
margin-left: 8px;
flex: 0 0 auto;
}
// menu hover
.submenu-title-noDropdown,
.el-submenu__title {
@ -90,7 +118,7 @@
& .nest-menu .el-submenu>.el-submenu__title,
& .el-submenu .el-menu-item {
min-width: $base-sidebar-width !important;
min-width: var(--sidebar-expanded-width, #{$base-sidebar-width}) !important;
&:hover {
background-color: rgba(0, 0, 0, 0.06) !important;
@ -109,11 +137,11 @@
.hideSidebar {
.sidebar-container {
width: 54px !important;
width: var(--sidebar-collapsed-width, 54px) !important;
}
.main-container {
margin-left: 54px;
margin-left: var(--sidebar-collapsed-width, 54px);
}
.submenu-title-noDropdown {
@ -121,10 +149,13 @@
position: relative;
.el-tooltip {
display: flex !important;
align-items: center;
padding: 0 !important;
.svg-icon {
margin-left: 20px;
margin-right: 0;
}
}
}
@ -137,6 +168,7 @@
.svg-icon {
margin-left: 20px;
margin-right: 0;
}
}
@ -158,7 +190,7 @@
}
.el-menu--collapse .el-menu .el-submenu {
min-width: $base-sidebar-width !important;
min-width: var(--sidebar-expanded-width, #{$base-sidebar-width}) !important;
}
// mobile responsive
@ -169,14 +201,14 @@
.sidebar-container {
transition: transform .28s;
width: $base-sidebar-width !important;
width: var(--sidebar-expanded-width, #{$base-sidebar-width}) !important;
}
&.hideSidebar {
.sidebar-container {
pointer-events: none;
transition-duration: 0.3s;
transform: translate3d(-$base-sidebar-width, 0, 0);
transform: translate3d(calc(-1 * var(--sidebar-expanded-width, #{$base-sidebar-width})), 0, 0);
}
}
}

1617
src/components/device/TrajectoryDialog.vue

File diff suppressed because it is too large

70
src/components/device/index.vue

@ -1,6 +1,6 @@
<template>
<el-dialog
title="认领设备"
:title="$t('device.claim.dialogTitle')"
:visible.sync="dialogVisible"
width="980px"
append-to-body
@ -10,28 +10,28 @@
ref="queryForm"
:model="queryParams"
:inline="true"
label-width="80px"
class="claim-query-form"
label-position="top"
class="page-query-form claim-query-form"
>
<el-form-item label="订单号" prop="orderCode">
<el-form-item :label="$t('device.claim.query.orderCode')" prop="orderCode">
<el-input
v-model.trim="queryParams.orderCode"
placeholder="请输入订单号"
:placeholder="$t('device.claim.placeholder.orderCode')"
clearable
size="small"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-form-item class="page-query-actions">
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">
搜索
{{ $t("common.search") }}
</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">{{ $t("common.reset") }}</el-button>
</el-form-item>
</el-form>
<el-alert
title="请先输入订单号查询设备,再勾选需要认领的设备进行确认。"
:title="$t('device.claim.tip')"
type="info"
:closable="false"
show-icon
@ -48,8 +48,8 @@
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" align="center" :reserve-selection="true" />
<el-table-column label="设备ID" prop="id" width="90" align="center" />
<el-table-column label="序列号" prop="sn" min-width="180" align="center" />
<el-table-column :label="$t('device.dialog.detail.id')" prop="id" width="90" align="center" />
<el-table-column :label="$t('device.table.sn')" prop="sn" min-width="180" align="center" />
<!-- <el-table-column label="操作" width="100" align="center" fixed="right">
<template slot-scope="scope">
<el-button type="text" size="mini" @click="handleDetail(scope.row)">详情</el-button>
@ -59,7 +59,7 @@
<el-empty
v-if="searched && !loading && !deviceList.length"
description="未查询到可认领的设备"
:description="$t('device.claim.empty')"
:image-size="80"
class="claim-empty"
/>
@ -73,46 +73,46 @@
/>
<div slot="footer" class="dialog-footer">
<span class="claim-selected">已选 {{ selectedIds.length }} 台设备</span>
<el-button @click="handleClose"> </el-button>
<span class="page-footer-meta">{{ $t("device.claim.selectedCount", { count: selectedIds.length }) }}</span>
<el-button @click="handleClose">{{ $t("common.cancel") }}</el-button>
<el-button type="primary" :loading="submitLoading" @click="handleSubmit">
确认认领
{{ $t("device.claim.confirmButton") }}
</el-button>
</div>
<el-dialog
title="设备详情"
:title="$t('device.claim.detailTitle')"
:visible.sync="detailOpen"
width="720px"
append-to-body
>
<div v-loading="detailLoading">
<el-descriptions v-if="deviceDetail" :column="2" border>
<el-descriptions-item label="设备ID">
<el-descriptions-item :label="$t('device.dialog.detail.id')">
{{ deviceDetail.id || "-" }}
</el-descriptions-item>
<el-descriptions-item label="订单号">
<el-descriptions-item :label="$t('device.table.orderCode')">
{{ deviceDetail.orderCode || "-" }}
</el-descriptions-item>
<el-descriptions-item label="序列号">
<el-descriptions-item :label="$t('device.table.sn')">
{{ deviceDetail.sn || "-" }}
</el-descriptions-item>
<el-descriptions-item label="MAC 地址">
<el-descriptions-item :label="$t('device.dialog.detail.macAddress')">
{{ deviceDetail.mac || "-" }}
</el-descriptions-item>
<el-descriptions-item label="所属批次">
<el-descriptions-item :label="$t('device.claim.detail.batchNo')">
{{ deviceDetail.batchNo || "-" }}
</el-descriptions-item>
<el-descriptions-item label="型号">
<el-descriptions-item :label="$t('device.table.model')">
{{ deviceDetail.model || "-" }}
</el-descriptions-item>
<el-descriptions-item label="企业">
<el-descriptions-item :label="$t('device.dialog.detail.business')">
{{ deviceDetail.merchantName || "-" }}
</el-descriptions-item>
</el-descriptions>
</div>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="detailOpen = false"> </el-button>
<el-button type="primary" @click="detailOpen = false">{{ $t("device.button.close") }}</el-button>
</div>
</el-dialog>
</el-dialog>
@ -161,7 +161,7 @@ export default {
methods: {
handleQuery() {
if (!this.queryParams.orderCode) {
this.$message.warning("请输入订单号后再搜索");
this.$message.warning(this.$t("device.claim.message.enterOrderCode"));
return;
}
this.searched = true;
@ -218,19 +218,19 @@ export default {
},
handleSubmit() {
if (!this.queryParams.orderCode) {
this.$message.warning("请先输入订单号并查询设备");
this.$message.warning(this.$t("device.claim.message.queryFirst"));
return;
}
if (!this.selectedIds.length) {
this.$message.warning("请至少选择一台设备");
this.$message.warning(this.$t("device.claim.message.selectAtLeastOne"));
return;
}
this.$confirm(
`确认认领当前选中的 ${this.selectedIds.length} 台设备吗?`,
"提示",
this.$t("device.claim.message.confirmClaim", { count: this.selectedIds.length }),
this.$t("common.tips"),
{
confirmButtonText: "确定",
cancelButtonText: "取消",
confirmButtonText: this.$t("common.confirm"),
cancelButtonText: this.$t("common.cancel"),
type: "warning",
}
)
@ -242,7 +242,7 @@ export default {
});
})
.then(() => {
this.$message.success("认领成功");
this.$message.success(this.$t("device.claim.message.claimSuccess"));
this.$emit("success");
this.handleClose();
})
@ -305,10 +305,4 @@ export default {
.claim-empty {
margin-top: 12px;
}
.claim-selected {
float: left;
line-height: 32px;
color: #606266;
}
</style>

1004
src/components/user/index.vue

File diff suppressed because it is too large

4
src/layout/components/Navbar.vue

@ -11,9 +11,9 @@
<screenfull id="screenfull" class="right-menu-item hover-effect" />
<el-tooltip :content="$t('navbar.layoutSize')" effect="dark" placement="bottom">
<!-- <el-tooltip :content="$t('navbar.layoutSize')" effect="dark" placement="bottom">
<size-select id="size-select" class="right-menu-item hover-effect" />
</el-tooltip>
</el-tooltip> -->
<el-dropdown class="right-menu-item hover-effect lang-menu" trigger="click" @command="changeLanguage">
<span class="lang-trigger">

16
src/layout/components/Sidebar/Item.vue

@ -17,15 +17,19 @@ export default {
const vnodes = []
if (icon) {
vnodes.push(<svg-icon icon-class={icon}/>)
vnodes.push(
<span class='menu-item-icon'>
<svg-icon icon-class={icon}/>
</span>
)
}
if (title) {
if (title.length > 5) {
vnodes.push(<span slot='title' title={(title)}>{(title)}</span>)
} else {
vnodes.push(<span slot='title'>{(title)}</span>)
}
vnodes.push(
<span slot='title' class='menu-item-text' title={title}>
{title}
</span>
)
}
return vnodes
}

54
src/layout/index.vue

@ -1,5 +1,5 @@
<template>
<div :class="classObj" class="app-wrapper" :style="{'--current-color': theme}">
<div :class="classObj" class="app-wrapper" :style="layoutStyle">
<div v-if="device==='mobile'&&sidebar.opened" class="drawer-bg" @click="handleClickOutside"/>
<sidebar v-if="!sidebar.hide" class="sidebar-container"/>
<div :class="{hasTagsView:needTagsView,sidebarHide:sidebar.hide}" class="main-container">
@ -16,7 +16,7 @@
<script>
import { AppMain, Navbar, Settings, Sidebar, TagsView } from './components'
import ResizeMixin from './mixin/ResizeHandler'
import { mapState } from 'vuex'
import { mapGetters, mapState } from 'vuex'
import variables from '@/assets/styles/variables.scss'
export default {
@ -38,6 +38,9 @@ export default {
needTagsView: state => state.settings.tagsView,
fixedHeader: state => state.settings.fixedHeader
}),
...mapGetters([
'sidebarRouters'
]),
classObj() {
return {
hideSidebar: !this.sidebar.opened,
@ -46,11 +49,54 @@ export default {
mobile: this.device === 'mobile'
}
},
layoutStyle() {
return {
'--current-color': this.theme,
'--sidebar-expanded-width': `${this.sidebarExpandedWidth}px`,
'--sidebar-collapsed-width': '54px'
}
},
sidebarExpandedWidth() {
const minWidth = 220
const maxWidth = 320
const extraWidth = 110
const titles = this.collectSidebarTitles(this.sidebarRouters)
const maxTitleWidth = titles.reduce((width, title) => {
return Math.max(width, this.measureSidebarTextWidth(title))
}, 0)
return Math.min(maxWidth, Math.max(minWidth, Math.ceil(maxTitleWidth + extraWidth)))
},
variables() {
return variables
}
},
methods: {
collectSidebarTitles(routes = [], titles = []) {
routes.forEach((route) => {
if (route && route.meta && route.meta.title) {
titles.push(String(route.meta.title))
}
if (route && Array.isArray(route.children) && route.children.length) {
this.collectSidebarTitles(route.children, titles)
}
})
return titles
},
measureSidebarTextWidth(text) {
const content = String(text || '')
if (!content) {
return 0
}
if (typeof document === 'undefined') {
return content.length * 14
}
if (!this._sidebarMeasureCanvas) {
this._sidebarMeasureCanvas = document.createElement('canvas')
this._sidebarMeasureContext = this._sidebarMeasureCanvas.getContext('2d')
}
this._sidebarMeasureContext.font = '14px "Helvetica Neue", Helvetica, Arial, sans-serif'
return this._sidebarMeasureContext.measureText(content).width
},
handleClickOutside() {
this.$store.dispatch('app/closeSideBar', { withoutAnimation: false })
},
@ -92,12 +138,12 @@ export default {
top: 0;
right: 0;
z-index: 9;
width: calc(100% - #{$base-sidebar-width});
width: calc(100% - var(--sidebar-expanded-width, #{$base-sidebar-width}));
transition: width 0.28s;
}
.hideSidebar .fixed-header {
width: calc(100% - 54px);
width: calc(100% - var(--sidebar-collapsed-width, 54px));
}
.sidebarHide .fixed-header {

20
src/main.js

@ -3,6 +3,12 @@ import Vue from "vue";
import Cookies from "js-cookie";
import Element from "element-ui";
import ElementLocale from "element-ui/lib/locale";
import elementZhCN from "element-ui/lib/locale/lang/zh-CN";
import elementEn from "element-ui/lib/locale/lang/en";
import elementFr from "element-ui/lib/locale/lang/fr";
import elementEs from "element-ui/lib/locale/lang/es";
import elementPtBr from "element-ui/lib/locale/lang/pt-br";
import "./assets/styles/element-variables.scss";
import "@/assets/styles/index.scss"; // global css
@ -16,6 +22,18 @@ import { download } from "@/utils/ruoyi";
import { math } from "@/utils/math.js";
import I18nPlugin from "@/lang";
import { getLanguage } from "@/utils/language";
const ELEMENT_LOCALE_MAP = {
"zh-CN": elementZhCN,
"en-US": elementEn,
"fr-FR": elementFr,
"es-ES": elementEs,
"pt-BR": elementPtBr,
};
const currentLanguage = getLanguage();
ElementLocale.use(ELEMENT_LOCALE_MAP[currentLanguage] || elementZhCN);
Vue.prototype.$math = math;
Vue.prototype.msgSuccess = function (msg) {
this.$message({ showClose: true, message: msg, type: "success" });
@ -106,7 +124,7 @@ Vue.use(Element, {
Vue.config.productionTip = false;
if (typeof document !== "undefined") {
document.documentElement.setAttribute("lang", getLanguage());
document.documentElement.setAttribute("lang", currentLanguage);
}
new Vue({

2
src/settings.js

@ -12,7 +12,7 @@ module.exports = {
/**
* 系统布局配置
*/
showSettings: true,
showSettings: false,
/**
* 是否显示顶部导航

370
src/views/device/device/index.vue

@ -1,6 +1,14 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px" @submit.native.prevent="handleQuery">
<el-form
:model="queryParams"
ref="queryForm"
:inline="true"
v-show="showSearch"
class="page-query-form device-query-form"
label-position="top"
@submit.native.prevent="handleQuery"
>
<el-form-item :label="$t('device.query.orderCode')" prop="orderCode">
<el-input v-model="queryParams.orderCode" :placeholder="$t('device.placeholder.orderCode')" clearable size="small" @keyup.enter.native="handleQuery" />
@ -32,13 +40,13 @@
</el-select>
</el-form-item>
<el-form-item>
<el-form-item class="page-query-actions">
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">{{ $t("common.search") }}</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">{{ $t("common.reset") }}</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-row :gutter="10" class="mb8 page-toolbar">
<!-- <el-col :span="1.5">
<el-button
type="primary"
@ -136,7 +144,7 @@
<span>{{ formatCoordinates(scope.row.lastLat, scope.row.lastLng) }}</span>
</template>
</el-table-column>
<el-table-column label="MAC " align="center" prop="mac" />
<el-table-column :label="$t('device.table.mac')" align="center" prop="mac" />
<!-- <el-table-column label="电量" align="center" prop="battery" /> -->
<el-table-column :label="$t('device.table.remark')" align="center" prop="remark" />
@ -175,19 +183,40 @@
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
<!-- Excel导入弹窗仅允许上传1个文件 + 手动输入订单号/备注 -->
<el-dialog :title="$t('device.dialog.import.title')" :visible.sync="importOpen" width="550px" append-to-body>
<el-form ref="importForm" :model="importForm" :rules="importRules" label-width="90px" @submit.native.prevent>
<el-dialog :title="$t('device.dialog.import.title')" :visible.sync="importOpen" width="680px" append-to-body>
<div class="device-import-guide">
<div class="device-import-guide__content">
<div class="device-import-guide__title">{{ $t("device.dialog.import.templateTitle") }}</div>
<div class="device-import-guide__desc">{{ $t("device.dialog.import.templateDesc") }}</div>
<div class="device-import-guide__meta">
<span>{{ $t("device.dialog.import.fieldExample") }}</span>
<span>{{ $t("device.dialog.import.sampleExample") }}</span>
</div>
</div>
<el-button type="primary" plain icon="el-icon-download" @click="downloadImportTemplate">
{{ $t("device.dialog.import.downloadTemplate") }}
</el-button>
</div>
<!-- Excel文件上传仅1个文件修复核心 -->
<el-form-item :label="$t('device.dialog.import.fileLabel')">
<el-upload ref="upload" :limit="1" :on-exceed="handleExceed" accept=".xlsx, .xls" :auto-upload="false" :on-change="handleFileChange" :file-list="fileList" drag>
<el-form ref="importForm" :model="importForm" :rules="importRules" label-position="top" class="page-dialog-form" @submit.native.prevent>
<el-form-item :label="$t('device.dialog.import.fileLabel')" prop="file">
<el-upload
ref="upload"
class="device-import-upload"
:limit="1"
:on-exceed="handleExceed"
accept=".xlsx, .xls"
:auto-upload="false"
:on-change="handleFileChange"
:file-list="fileList"
drag
>
<i class="el-icon-upload"></i>
<div class="el-upload__text">{{ $t("device.dialog.import.dragText") }}<em>{{ $t("device.dialog.import.clickUpload") }}</em></div>
<div class="el-upload__text">
{{ $t("device.dialog.import.dragText") }}<em>{{ $t("device.dialog.import.clickUpload") }}</em>
</div>
<div class="el-upload__tip" slot="tip">
<el-icon size="12">
<warning />
</el-icon>
<span>{{ $t("device.dialog.import.tip") }}</span>
{{ $t("device.dialog.import.tip") }}
</div>
</el-upload>
</el-form-item>
@ -199,43 +228,52 @@
</div>
</el-dialog>
<el-dialog :title="$t('device.dialog.importResult.title')" :visible.sync="resultOpen" width="700px" append-to-body>
<div v-if="latestImportResult">
<el-alert :title="getImportStatusMessage(latestImportResult)" :type="getImportStatusType(latestImportResult.status)" :closable="false" show-icon />
<el-dialog :title="$t('device.dialog.importResult.title')" :visible.sync="resultOpen" width="760px" append-to-body>
<div v-if="latestImportResult" class="device-import-result">
<el-alert
class="device-import-result__alert"
:title="getImportStatusMessage(latestImportResult)"
:type="getImportStatusType(latestImportResult.status)"
:closable="false"
show-icon
/>
<el-descriptions :column="2" border class="import-result-summary">
<el-descriptions-item :label="$t('device.dialog.importResult.status')">
{{ getImportStatusLabel(latestImportResult.status) }}
</el-descriptions-item>
<el-descriptions-item :label="$t('device.dialog.importResult.total')">
{{ latestImportResult.total }}
</el-descriptions-item>
<el-descriptions-item :label="$t('device.dialog.importResult.successCount')">
{{ latestImportResult.successCount }}
</el-descriptions-item>
<el-descriptions-item :label="$t('device.dialog.importResult.failCount')">
{{ latestImportResult.failCount }}
</el-descriptions-item>
<el-descriptions-item :label="$t('device.dialog.importResult.startTime')">
{{ parseTime(latestImportResult.startTime) }}
</el-descriptions-item>
<el-descriptions-item :label="$t('device.dialog.importResult.finishTime')">
{{ parseTime(latestImportResult.finishTime) }}
</el-descriptions-item>
</el-descriptions>
<div class="device-import-result__panel">
<div class="import-result-title">{{ $t("device.dialog.importResult.title") }}</div>
<el-descriptions :column="importResultColumns" border class="import-result-summary">
<el-descriptions-item :label="$t('device.dialog.importResult.status')">
{{ getImportStatusLabel(latestImportResult.status) }}
</el-descriptions-item>
<el-descriptions-item :label="$t('device.dialog.importResult.total')">
{{ latestImportResult.total }}
</el-descriptions-item>
<el-descriptions-item :label="$t('device.dialog.importResult.successCount')">
{{ latestImportResult.successCount }}
</el-descriptions-item>
<el-descriptions-item :label="$t('device.dialog.importResult.failCount')">
{{ latestImportResult.failCount }}
</el-descriptions-item>
<el-descriptions-item :label="$t('device.dialog.importResult.startTime')">
{{ parseTime(latestImportResult.startTime) }}
</el-descriptions-item>
<el-descriptions-item :label="$t('device.dialog.importResult.finishTime')">
{{ parseTime(latestImportResult.finishTime) }}
</el-descriptions-item>
</el-descriptions>
</div>
<div v-if="latestImportResult.requestErrors && latestImportResult.requestErrors.length" class="import-result-block">
<div v-if="latestImportResult.requestErrors && latestImportResult.requestErrors.length" class="import-result-block device-import-result__panel">
<div class="import-result-title">{{ $t("device.dialog.importResult.requestErrors") }}</div>
<div v-for="(item, index) in latestImportResult.requestErrors" :key="'request-error-' + index" class="import-result-text">
{{ index + 1 }}. {{ item }}
</div>
</div>
<div v-if="latestImportResult.errors && latestImportResult.errors.length" class="import-result-block">
<div v-if="latestImportResult.errors && latestImportResult.errors.length" class="import-result-block device-import-result__panel">
<div class="import-result-title">{{ $t("device.dialog.importResult.failDetails") }}</div>
<el-table :data="latestImportResult.errors" size="mini" border max-height="320">
<el-table :data="latestImportResult.errors" size="mini" border max-height="320" class="device-import-result__table">
<el-table-column :label="$t('device.dialog.importResult.rowIndex')" prop="rowIndex" width="100" align="center" />
<el-table-column :label="$t('device.dialog.importResult.errorMessage')" prop="message" min-width="460" />
<el-table-column :label="$t('device.dialog.importResult.errorMessage')" prop="message" min-width="460" show-overflow-tooltip />
</el-table>
</div>
</div>
@ -251,7 +289,7 @@
<el-dialog :title="$t('device.dialog.assign.title')" :visible.sync="assignDeviceOpen" width="1200px" append-to-body @close="handleAssignDialogClose">
<UserSelector ref="userSelector" selection-mode @select="handleAssignUserSelect" />
<div slot="footer" class="dialog-footer">
<span class="assign-selected-user">
<span class="page-footer-meta">
{{ $t("device.dialog.assign.selectedUsers", { count: selectedAssignUsers.length }) }}
</span>
<el-button @click="handleAssignDialogClose">{{ $t("common.cancel") }}</el-button>
@ -263,7 +301,7 @@
<el-dialog :title="$t('device.dialog.detail.title')" :visible.sync="detailOpen" width="720px" append-to-body>
<div v-loading="detailLoading">
<el-descriptions v-if="detailForm" :column="2" border>
<el-descriptions-item label="ID">{{ detailForm.id || "-" }}</el-descriptions-item>
<el-descriptions-item :label="$t('device.dialog.detail.id')">{{ detailForm.id || "-" }}</el-descriptions-item>
<el-descriptions-item :label="$t('device.table.sn')">{{ detailForm.sn || "-" }}</el-descriptions-item>
<el-descriptions-item :label="$t('device.dialog.detail.macAddress')">{{ detailForm.mac || "-" }}</el-descriptions-item>
<el-descriptions-item :label="$t('device.table.model')">{{ detailForm.model || "-" }}</el-descriptions-item>
@ -296,17 +334,19 @@
<el-button type="primary" @click="detailOpen = false">{{ $t("device.button.close") }}</el-button>
</div>
</el-dialog>
<el-dialog :title="$t('device.dialog.editInfo.title')" :visible.sync="editInfoOpen" width="520px" append-to-body @close="cancelEditInfo">
<el-form ref="editInfoForm" :model="editInfoForm" :rules="editInfoRules" label-width="100px">
<el-form-item :label="$t('device.table.sn')">
<el-input :value="editInfoForm.sn || '-'" disabled />
</el-form-item>
<el-form-item :label="$t('device.table.alias')" prop="alias">
<el-input v-model="editInfoForm.alias" :placeholder="$t('device.placeholder.alias')" clearable maxlength="64" show-word-limit />
</el-form-item>
<el-form-item :label="$t('device.query.remark')" prop="remark">
<el-input v-model="editInfoForm.remark" type="textarea" :rows="4" :placeholder="$t('device.placeholder.remark')" maxlength="255" show-word-limit />
</el-form-item>
<el-dialog :title="$t('device.dialog.editInfo.title')" :visible.sync="editInfoOpen" width="560px" append-to-body @close="cancelEditInfo">
<el-form ref="editInfoForm" :model="editInfoForm" :rules="editInfoRules" label-position="top" class="page-dialog-form">
<div class="page-dialog-grid">
<el-form-item :label="$t('device.table.sn')">
<el-input :value="editInfoForm.sn || '-'" disabled />
</el-form-item>
<el-form-item :label="$t('device.table.alias')" prop="alias">
<el-input v-model="editInfoForm.alias" :placeholder="$t('device.placeholder.alias')" clearable maxlength="64" show-word-limit />
</el-form-item>
<el-form-item :label="$t('device.query.remark')" prop="remark" class="page-dialog-grid__full">
<el-input v-model="editInfoForm.remark" type="textarea" :rows="4" :placeholder="$t('device.placeholder.remark')" maxlength="255" show-word-limit />
</el-form-item>
</div>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="cancelEditInfo">{{ $t("common.cancel") }}</el-button>
@ -315,62 +355,62 @@
</el-dialog>
<DeviceTrajectoryDialog :visible.sync="trajectoryOpen" :device="trajectoryDevice" />
<!-- 添加或修改系统设备主对话框 -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item :label="$t('device.table.sn')" prop="sn">
<el-input v-model="form.sn" :placeholder="$t('device.placeholder.sn')" />
</el-form-item>
<el-form-item :label="$t('device.dialog.detail.macAddress')" prop="mac">
<el-input v-model="form.mac" :placeholder="$t('device.placeholder.macAddress')" />
</el-form-item>
<!-- 新增订单号表单项 -->
<el-form-item :label="$t('device.table.orderCode')" prop="orderCode">
<el-input v-model="form.orderCode" :placeholder="$t('device.placeholder.orderCode')" />
</el-form-item>
<el-form-item :label="$t('device.form.privateKey')" prop="privateKey">
<el-input v-model="form.privateKey" :placeholder="$t('device.placeholder.privateKey')" />
</el-form-item>
<el-form-item :label="$t('device.form.batchNo')" prop="batchNo">
<el-input v-model="form.batchNo" :placeholder="$t('device.placeholder.batchNo')" />
</el-form-item>
<el-form-item :label="$t('device.form.hashId')" prop="hashid">
<el-input v-model="form.hashid" :placeholder="$t('device.placeholder.hashId')" />
</el-form-item>
<el-form-item :label="$t('device.table.model')" prop="model">
<el-input v-model="form.model" :placeholder="$t('device.placeholder.model')" />
</el-form-item>
<el-form-item :label="$t('device.form.bindBusinessId')" prop="bindBusinessId">
<el-input v-model="form.bindBusinessId" :placeholder="$t('device.placeholder.bindBusinessId')" />
</el-form-item>
<!-- 新增备注表单项 -->
<el-form-item :label="$t('device.table.remark')" prop="remark">
<el-input v-model="form.remark" :placeholder="$t('device.placeholder.remarkSimple')" type="textarea" :rows="3" />
</el-form-item>
<el-form-item :label="$t('device.form.locateUpdateTime')" prop="locateUpdateTime">
<el-date-picker clearable size="small" v-model="form.locateUpdateTime" type="date" value-format="yyyy-MM-dd" :placeholder="$t('device.placeholder.locateUpdateTime')">
</el-date-picker>
</el-form-item>
<el-form-item :label="$t('device.form.lastLat')" prop="lastLat">
<el-input v-model="form.lastLat" :placeholder="$t('device.placeholder.lastLat')" />
</el-form-item>
<el-form-item :label="$t('device.form.lastLng')" prop="lastLng">
<el-input v-model="form.lastLng" :placeholder="$t('device.placeholder.lastLng')" />
</el-form-item>
<el-form-item :label="$t('device.dialog.detail.battery')" prop="battery">
<el-input v-model="form.battery" :placeholder="$t('device.placeholder.battery')" />
</el-form-item>
<el-form-item :label="$t('device.dialog.detail.lastReportedTime')" prop="lastReportedTime">
<el-date-picker clearable size="small" v-model="form.lastReportedTime" type="date" value-format="yyyy-MM-dd" :placeholder="$t('device.placeholder.lastReportedTime')">
</el-date-picker>
</el-form-item>
<el-form-item :label="$t('device.dialog.detail.lastLocationTime')" prop="lastLocationTime">
<el-date-picker clearable size="small" v-model="form.lastLocationTime" type="date" value-format="yyyy-MM-dd" :placeholder="$t('device.placeholder.lastLocationTime')">
</el-date-picker>
</el-form-item>
<el-form-item :label="$t('common.createTime')" prop="createTime">
<el-date-picker clearable size="small" v-model="form.createTime" type="date" value-format="yyyy-MM-dd" :placeholder="$t('device.placeholder.createTime')">
</el-date-picker>
</el-form-item>
<el-dialog :title="title" :visible.sync="open" width="820px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-position="top" class="page-dialog-form">
<div class="page-dialog-grid">
<el-form-item :label="$t('device.table.sn')" prop="sn">
<el-input v-model="form.sn" :placeholder="$t('device.placeholder.sn')" />
</el-form-item>
<el-form-item :label="$t('device.dialog.detail.macAddress')" prop="mac">
<el-input v-model="form.mac" :placeholder="$t('device.placeholder.macAddress')" />
</el-form-item>
<el-form-item :label="$t('device.table.orderCode')" prop="orderCode">
<el-input v-model="form.orderCode" :placeholder="$t('device.placeholder.orderCode')" />
</el-form-item>
<el-form-item :label="$t('device.form.privateKey')" prop="privateKey">
<el-input v-model="form.privateKey" :placeholder="$t('device.placeholder.privateKey')" />
</el-form-item>
<el-form-item :label="$t('device.form.batchNo')" prop="batchNo">
<el-input v-model="form.batchNo" :placeholder="$t('device.placeholder.batchNo')" />
</el-form-item>
<el-form-item :label="$t('device.form.hashId')" prop="hashid">
<el-input v-model="form.hashid" :placeholder="$t('device.placeholder.hashId')" />
</el-form-item>
<el-form-item :label="$t('device.table.model')" prop="model">
<el-input v-model="form.model" :placeholder="$t('device.placeholder.model')" />
</el-form-item>
<el-form-item :label="$t('device.form.bindBusinessId')" prop="bindBusinessId">
<el-input v-model="form.bindBusinessId" :placeholder="$t('device.placeholder.bindBusinessId')" />
</el-form-item>
<el-form-item :label="$t('device.table.remark')" prop="remark" class="page-dialog-grid__full">
<el-input v-model="form.remark" :placeholder="$t('device.placeholder.remarkSimple')" type="textarea" :rows="3" />
</el-form-item>
<el-form-item :label="$t('device.form.locateUpdateTime')" prop="locateUpdateTime">
<el-date-picker clearable size="small" v-model="form.locateUpdateTime" type="date" value-format="yyyy-MM-dd" :placeholder="$t('device.placeholder.locateUpdateTime')">
</el-date-picker>
</el-form-item>
<el-form-item :label="$t('device.form.lastLat')" prop="lastLat">
<el-input v-model="form.lastLat" :placeholder="$t('device.placeholder.lastLat')" />
</el-form-item>
<el-form-item :label="$t('device.form.lastLng')" prop="lastLng">
<el-input v-model="form.lastLng" :placeholder="$t('device.placeholder.lastLng')" />
</el-form-item>
<el-form-item :label="$t('device.dialog.detail.battery')" prop="battery">
<el-input v-model="form.battery" :placeholder="$t('device.placeholder.battery')" />
</el-form-item>
<el-form-item :label="$t('device.dialog.detail.lastReportedTime')" prop="lastReportedTime">
<el-date-picker clearable size="small" v-model="form.lastReportedTime" type="date" value-format="yyyy-MM-dd" :placeholder="$t('device.placeholder.lastReportedTime')">
</el-date-picker>
</el-form-item>
<el-form-item :label="$t('device.dialog.detail.lastLocationTime')" prop="lastLocationTime">
<el-date-picker clearable size="small" v-model="form.lastLocationTime" type="date" value-format="yyyy-MM-dd" :placeholder="$t('device.placeholder.lastLocationTime')">
</el-date-picker>
</el-form-item>
<el-form-item :label="$t('common.createTime')" prop="createTime">
<el-date-picker clearable size="small" v-model="form.createTime" type="date" value-format="yyyy-MM-dd" :placeholder="$t('device.placeholder.createTime')">
</el-date-picker>
</el-form-item>
</div>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm">{{ $t("common.confirm") }}</el-button>
@ -408,6 +448,7 @@ import UserSelector from "@/components/user";
function getDefaultImportForm() {
return {
file: null,
orderCode: "",
batchNo: "",
remark: "",
@ -603,6 +644,11 @@ export default {
created() {
this.getList();
},
computed: {
importResultColumns() {
return this.$store.getters.device === "mobile" ? 1 : 2;
},
},
deactivated() {
this.closeBusinessSelectDialogs();
},
@ -1052,6 +1098,10 @@ export default {
});
});
},
downloadImportTemplate() {
const templateUrl = `${process.env.BASE_URL || "/"}templates/device-import-template.xlsx`;
window.open(templateUrl, "_blank");
},
/** 限制文件数量:超出1个时提示 */
handleExceed(files, fileList) {
this.$message.warning(this.$t("device.message.exceedFileLimit", { count: fileList.length }));
@ -1076,6 +1126,7 @@ export default {
this.fileList = fileList.slice(-1);
// 1
this.uploadFile = this.fileList.length > 0 ? this.fileList[0].raw : null;
this.importForm.file = this.uploadFile;
},
/** 提交导入 */
@ -1210,12 +1261,102 @@ export default {
</script>
<style scoped>
.device-import-result {
display: flex;
flex-direction: column;
gap: 16px;
}
.device-import-result__alert {
margin-bottom: 0;
}
.device-import-result__panel {
padding: 16px;
border: 1px solid #ebeef5;
border-radius: 8px;
background: #fff;
}
.device-import-guide {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
justify-content: space-between;
gap: 12px 16px;
padding: 14px 16px;
margin-bottom: 16px;
border: 1px solid #ebeef5;
border-radius: 8px;
background: #f8fafc;
}
.device-import-guide__content {
flex: 1;
min-width: 260px;
}
.device-import-guide__title {
margin-bottom: 6px;
font-size: 14px;
font-weight: 600;
color: #303133;
}
.device-import-guide__desc {
line-height: 1.6;
color: #606266;
}
.device-import-guide__meta {
display: flex;
flex-wrap: wrap;
gap: 6px 16px;
margin-top: 8px;
font-size: 12px;
color: #909399;
}
.device-import-upload :deep(.el-upload) {
width: 100%;
}
.device-import-upload :deep(.el-upload-dragger) {
width: 100%;
height: auto;
min-height: 180px;
padding: 24px 16px;
}
.device-import-upload :deep(.el-upload__text) {
white-space: normal;
word-break: break-word;
line-height: 1.7;
}
.device-import-upload :deep(.el-upload__tip) {
white-space: normal;
line-height: 1.6;
}
.import-result-summary {
margin-top: 16px;
margin-top: 0;
}
.import-result-summary :deep(.el-descriptions-item__label) {
white-space: normal;
line-height: 20px;
word-break: break-word;
}
.device-import-result__table :deep(.cell) {
white-space: normal;
word-break: break-word;
line-height: 1.6;
}
.import-result-block {
margin-top: 16px;
margin-top: 0;
}
.import-result-title {
@ -1230,9 +1371,4 @@ export default {
color: #606266;
}
.assign-selected-user {
float: left;
line-height: 32px;
color: #606266;
}
</style>

967
src/views/system/role/index.vue

@ -1,511 +1,506 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch">
<el-form-item label="角色名称" prop="roleName">
<el-input v-model="queryParams.roleName" placeholder="请输入角色名称" clearable style="width: 240px" @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="权限字符" prop="roleKey">
<el-input v-model="queryParams.roleKey" placeholder="请输入权限字符" clearable style="width: 240px" @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="角色状态" clearable style="width: 240px">
<el-option label="正常" :value="0" />
<el-option label="停用" :value="1" />
</el-select>
</el-form-item>
<!-- <el-form-item label="创建时间">
<el-date-picker v-model="dateRange" style="width: 240px" value-format="yyyy-MM-dd" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker>
</el-form-item> -->
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
<template>
<div class="app-container">
<el-form
ref="queryForm"
:model="queryParams"
size="small"
:inline="true"
v-show="showSearch"
class="page-query-form system-role-query-form"
label-position="top"
@submit.native.prevent="handleQuery"
>
<el-form-item :label="$t('systemRole.query.roleName')" prop="roleName">
<el-input
v-model="queryParams.roleName"
:placeholder="$t('systemRole.placeholder.roleName')"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item :label="$t('systemRole.query.roleKey')" prop="roleKey">
<el-input
v-model="queryParams.roleKey"
:placeholder="$t('systemRole.placeholder.roleKey')"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item :label="$t('systemRole.query.status')" prop="status">
<el-select v-model="queryParams.status" :placeholder="$t('systemRole.placeholder.status')" clearable>
<el-option :label="$t('systemRole.status.normal')" :value="0" />
<el-option :label="$t('systemRole.status.disabled')" :value="1" />
</el-select>
</el-form-item>
<el-form-item class="page-query-actions">
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">{{ $t("common.search") }}</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">{{ $t("common.reset") }}</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['system:role:add']">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="el-icon-edit" size="mini" :disabled="single" @click="handleUpdate" v-hasPermi="['system:role:edit']">修改</el-button>
</el-col>
<!-- <el-col :span="1.5">-->
<!-- <el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete" v-hasPermi="['system:role:remove']">删除</el-button>-->
<!-- </el-col>-->
<el-col :span="1.5">
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" v-hasPermi="['system:role:export']">导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
<el-row :gutter="10" class="mb8 page-toolbar">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['system:role:add']">
{{ $t("common.add") }}
</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-edit"
size="mini"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['system:role:edit']"
>
{{ $t("common.edit") }}
</el-button>
</el-col>
<el-col :span="1.5">
<!-- <el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
@click="handleExport"
v-hasPermi="['system:role:export']"
>
{{ $t("systemRole.button.export") }}
</el-button> -->
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList" />
</el-row>
<el-table v-loading="loading" :data="roleList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="角色编号" prop="roleId" width="120" />
<el-table-column label="角色名称" prop="roleName" :show-overflow-tooltip="true" width="150" />
<el-table-column label="权限字符" prop="roleKey" :show-overflow-tooltip="true" width="150" />
<el-table-column label="显示顺序" prop="roleSort" width="100" />
<el-table-column label="状态" align="center" width="100">
<template slot-scope="scope">
<el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope" v-if="scope.row.roleId !== 1">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:role:edit']">修改</el-button>
<!-- <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['system:role:remove']">删除</el-button>-->
<el-dropdown size="mini" @command="(command) => handleCommand(command, scope.row)" v-hasPermi="['system:role:edit']">
<!-- <el-button size="mini" type="text" icon="el-icon-d-arrow-right">更多</el-button> -->
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="handleDataScope" icon="el-icon-circle-check" v-hasPermi="['system:role:edit']">数据权限</el-dropdown-item>
<el-dropdown-item command="handleAuthUser" icon="el-icon-user" v-hasPermi="['system:role:edit']">分配用户</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
</el-table-column>
<el-table-column type="selection" width="55" align="center" />
<el-table-column :label="$t('systemRole.table.roleId')" prop="roleId" width="120" />
<el-table-column :label="$t('systemRole.table.roleName')" prop="roleName" :show-overflow-tooltip="true" width="150" />
<el-table-column :label="$t('systemRole.table.roleKey')" prop="roleKey" :show-overflow-tooltip="true" width="150" />
<el-table-column :label="$t('systemRole.table.roleSort')" prop="roleSort" width="100" />
<el-table-column :label="$t('systemRole.table.status')" align="center" width="100">
<template slot-scope="scope">
<el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)" />
</template>
</el-table-column>
<el-table-column :label="$t('systemRole.table.createTime')" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column :label="$t('systemRole.table.actions')" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope" v-if="scope.row.roleId !== 1">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:role:edit']">
{{ $t("common.edit") }}
</el-button>
<el-dropdown size="mini" @command="(command) => handleCommand(command, scope.row)" v-hasPermi="['system:role:edit']">
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="handleDataScope" icon="el-icon-circle-check" v-hasPermi="['system:role:edit']">
{{ $t("systemRole.dropdown.dataScope") }}
</el-dropdown-item>
<el-dropdown-item command="handleAuthUser" icon="el-icon-user" v-hasPermi="['system:role:edit']">
{{ $t("systemRole.dropdown.assignUser") }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
<!-- 添加或修改角色配置对话框 -->
<el-dialog :title="title" :visible.sync="open" width="800px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
<el-form-item label="角色名称" prop="roleName">
<el-input v-model="form.roleName" placeholder="请输入角色名称" />
</el-form-item>
<el-form-item prop="roleKey">
<span slot="label">
<el-tooltip content="控制器中定义的权限字符,如:@PreAuthorize(`@ss.hasRole('admin')`)" placement="top">
<i class="el-icon-question"></i>
</el-tooltip>
权限字符
</span>
<el-input v-model="form.roleKey" placeholder="请输入权限字符" />
</el-form-item>
<el-form-item label="角色顺序" prop="roleSort">
<el-input-number v-model="form.roleSort" controls-position="right" :min="0" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="form.status">
<el-radio :label="0">正常</el-radio>
<el-radio :label="1">停用</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="菜单权限">
<el-checkbox v-model="menuExpand" @change="handleCheckedTreeExpand($event, 'menu')">展开/折叠</el-checkbox>
<el-checkbox v-model="menuNodeAll" @change="handleCheckedTreeNodeAll($event, 'menu')">全选/全不选</el-checkbox>
<el-checkbox v-model="form.menuCheckStrictly" @change="handleCheckedTreeConnect($event, 'menu')">父子联动</el-checkbox>
<el-tree class="tree-border" :data="menuOptions" show-checkbox ref="menu" node-key="id" :check-strictly="!form.menuCheckStrictly" empty-text="加载中请稍候" :props="defaultProps"></el-tree>
</el-form-item>
<el-form-item label="备注">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
<el-dialog :title="title" :visible.sync="open" width="860px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-position="top" class="page-dialog-form">
<div class="page-dialog-grid">
<el-form-item :label="$t('systemRole.form.roleName')" prop="roleName">
<el-input v-model="form.roleName" :placeholder="$t('systemRole.placeholder.roleName')" />
</el-form-item>
<el-form-item prop="roleKey">
<span slot="label">
<el-tooltip :content="$t('systemRole.tooltip.roleKey')" placement="top">
<i class="el-icon-question"></i>
</el-tooltip>
{{ $t("systemRole.form.roleKey") }}
</span>
<el-input v-model="form.roleKey" :placeholder="$t('systemRole.placeholder.permissionKey')" />
</el-form-item>
<el-form-item :label="$t('systemRole.form.roleSort')" prop="roleSort">
<el-input-number v-model="form.roleSort" controls-position="right" :min="0" />
</el-form-item>
<el-form-item :label="$t('systemRole.form.status')" prop="status">
<el-radio-group v-model="form.status">
<el-radio :label="0">{{ $t("systemRole.status.normal") }}</el-radio>
<el-radio :label="1">{{ $t("systemRole.status.disabled") }}</el-radio>
</el-radio-group>
</el-form-item>
</div>
<el-form-item :label="$t('systemRole.form.menuPermission')">
<div class="page-tree-option-group">
<el-checkbox v-model="menuExpand" @change="handleCheckedTreeExpand($event, 'menu')">{{ $t("systemRole.tree.expandCollapse") }}</el-checkbox>
<el-checkbox v-model="menuNodeAll" @change="handleCheckedTreeNodeAll($event, 'menu')">{{ $t("systemRole.tree.selectAll") }}</el-checkbox>
<el-checkbox v-model="form.menuCheckStrictly" @change="handleCheckedTreeConnect($event, 'menu')">{{ $t("systemRole.tree.parentChildLinkage") }}</el-checkbox>
</div>
<el-tree
ref="menu"
class="tree-border"
:data="menuOptions"
show-checkbox
node-key="id"
:check-strictly="!form.menuCheckStrictly"
:empty-text="$t('systemRole.tree.loading')"
:props="defaultProps"
/>
</el-form-item>
<el-form-item :label="$t('systemRole.form.remark')">
<el-input v-model="form.remark" type="textarea" :placeholder="$t('systemRole.placeholder.remark')" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm">{{ $t("common.confirm") }}</el-button>
<el-button @click="cancel">{{ $t("common.cancel") }}</el-button>
</div>
</el-dialog>
<!-- 分配角色数据权限对话框 -->
<el-dialog :title="title" :visible.sync="openDataScope" width="800px" append-to-body>
<el-form :model="form" label-width="80px">
<el-form-item label="角色名称">
<el-input v-model="form.roleName" :disabled="true" />
</el-form-item>
<el-form-item label="权限字符">
<el-input v-model="form.roleKey" :disabled="true" />
</el-form-item>
<el-form-item label="权限范围">
<el-select v-model="form.dataScope" @change="dataScopeSelectChange">
<el-option v-for="item in dataScopeOptions" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item label="数据权限" v-show="form.dataScope == 2">
<el-checkbox v-model="deptExpand" @change="handleCheckedTreeExpand($event, 'dept')">展开/折叠</el-checkbox>
<el-checkbox v-model="deptNodeAll" @change="handleCheckedTreeNodeAll($event, 'dept')">全选/全不选</el-checkbox>
<el-checkbox v-model="form.deptCheckStrictly" @change="handleCheckedTreeConnect($event, 'dept')">父子联动</el-checkbox>
<el-tree class="tree-border" :data="deptOptions" show-checkbox default-expand-all ref="dept" node-key="id" :check-strictly="!form.deptCheckStrictly" empty-text="加载中请稍候" :props="defaultProps"></el-tree>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitDataScope"> </el-button>
<el-button @click="cancelDataScope"> </el-button>
<el-form :model="form" label-position="top" class="page-dialog-form">
<div class="page-dialog-grid">
<el-form-item :label="$t('systemRole.form.scopeRoleName')">
<el-input v-model="form.roleName" :disabled="true" />
</el-form-item>
<el-form-item :label="$t('systemRole.form.scopeRoleKey')">
<el-input v-model="form.roleKey" :disabled="true" />
</el-form-item>
<el-form-item :label="$t('systemRole.form.permissionRange')" class="page-dialog-grid__full">
<el-select v-model="form.dataScope" @change="dataScopeSelectChange">
<el-option v-for="item in dataScopeOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</div>
<el-form-item :label="$t('systemRole.form.dataPermission')" v-show="form.dataScope == 2">
<div class="page-tree-option-group">
<el-checkbox v-model="deptExpand" @change="handleCheckedTreeExpand($event, 'dept')">{{ $t("systemRole.tree.expandCollapse") }}</el-checkbox>
<el-checkbox v-model="deptNodeAll" @change="handleCheckedTreeNodeAll($event, 'dept')">{{ $t("systemRole.tree.selectAll") }}</el-checkbox>
<el-checkbox v-model="form.deptCheckStrictly" @change="handleCheckedTreeConnect($event, 'dept')">{{ $t("systemRole.tree.parentChildLinkage") }}</el-checkbox>
</div>
<el-tree
ref="dept"
class="tree-border"
:data="deptOptions"
show-checkbox
default-expand-all
node-key="id"
:check-strictly="!form.deptCheckStrictly"
:empty-text="$t('systemRole.tree.loading')"
:props="defaultProps"
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitDataScope">{{ $t("common.confirm") }}</el-button>
<el-button @click="cancelDataScope">{{ $t("common.cancel") }}</el-button>
</div>
</el-dialog>
</div>
</div>
</template>
<script>
import {
listRole,
getRole,
delRole,
addRole,
updateRole,
dataScope,
changeRoleStatus,
deptTreeSelect
} from "@/api/system/role"
import {
treeselect as menuTreeselect,
roleMenuTreeselect
} from "@/api/system/menu"
import { status } from "nprogress";
listRole,
getRole,
delRole,
addRole,
updateRole,
dataScope,
changeRoleStatus,
deptTreeSelect,
} from "@/api/system/role";
import { treeselect as menuTreeselect, roleMenuTreeselect } from "@/api/system/menu";
export default {
name: "Role",
dicts: [],
data() {
return {
//
loading: true,
//
ids: [],
//
single: true,
//
multiple: true,
//
showSearch: true,
//
total: 0,
//
roleList: [],
//
title: "",
//
open: false,
//
openDataScope: false,
menuExpand: false,
menuNodeAll: false,
deptExpand: true,
deptNodeAll: false,
//
dateRange: [],
//
dataScopeOptions: [{
value: "1",
label: "全部数据权限"
},
{
value: "2",
label: "自定数据权限"
},
{
value: "3",
label: "本部门数据权限"
},
{
value: "4",
label: "本部门及以下数据权限"
},
{
value: "5",
label: "仅本人数据权限"
}
],
//
menuOptions: [],
//
deptOptions: [],
//
queryParams: {
pageNum: 1,
pageSize: 10,
roleName: undefined,
roleKey: undefined,
status: undefined
},
//
form: {status: 0},
defaultProps: {
children: "children",
label: "label"
},
//
rules: {
roleName: [{
required: true,
message: "角色名称不能为空",
trigger: "blur"
}],
roleKey: [{
required: true,
message: "权限字符不能为空",
trigger: "blur"
}],
roleSort: [{
required: true,
message: "角色顺序不能为空",
trigger: "blur"
}]
}
name: "Role",
dicts: [],
data() {
return {
loading: true,
ids: [],
single: true,
multiple: true,
showSearch: true,
total: 0,
roleList: [],
title: "",
open: false,
openDataScope: false,
menuExpand: false,
menuNodeAll: false,
deptExpand: true,
deptNodeAll: false,
dateRange: [],
dataScopeOptions: [],
menuOptions: [],
deptOptions: [],
queryParams: {
pageNum: 1,
pageSize: 10,
roleName: undefined,
roleKey: undefined,
status: undefined,
},
form: {
status: 0,
},
defaultProps: {
children: "children",
label: "label",
},
rules: {},
};
},
created() {
this.initI18nState();
this.getList();
},
methods: {
initI18nState() {
this.dataScopeOptions = [
{ value: "1", label: this.$t("systemRole.dataScope.all") },
{ value: "2", label: this.$t("systemRole.dataScope.custom") },
{ value: "3", label: this.$t("systemRole.dataScope.dept") },
{ value: "4", label: this.$t("systemRole.dataScope.deptAndChild") },
{ value: "5", label: this.$t("systemRole.dataScope.self") },
];
this.rules = {
roleName: [{ required: true, message: this.$t("systemRole.validation.roleNameRequired"), trigger: "blur" }],
roleKey: [{ required: true, message: this.$t("systemRole.validation.roleKeyRequired"), trigger: "blur" }],
roleSort: [{ required: true, message: this.$t("systemRole.validation.roleSortRequired"), trigger: "blur" }],
};
},
getList() {
this.loading = true;
listRole(this.addDateRange(this.queryParams, this.dateRange)).then((response) => {
this.roleList = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
getMenuTreeselect() {
menuTreeselect().then((response) => {
this.menuOptions = response.data;
});
},
getMenuAllCheckedKeys() {
const checkedKeys = this.$refs.menu.getCheckedKeys();
const halfCheckedKeys = this.$refs.menu.getHalfCheckedKeys();
checkedKeys.unshift.apply(checkedKeys, halfCheckedKeys);
return checkedKeys;
},
getDeptAllCheckedKeys() {
const checkedKeys = this.$refs.dept.getCheckedKeys();
const halfCheckedKeys = this.$refs.dept.getHalfCheckedKeys();
checkedKeys.unshift.apply(checkedKeys, halfCheckedKeys);
return checkedKeys;
},
getRoleMenuTreeselect(roleId) {
return roleMenuTreeselect(roleId).then((response) => {
this.menuOptions = response.menus;
return response;
});
},
getDeptTree(roleId) {
return deptTreeSelect(roleId).then((response) => {
this.deptOptions = response.depts;
return response;
});
},
handleStatusChange(row) {
const text = row.status === "0" ? this.$t("systemRole.status.normal") : this.$t("systemRole.status.disabled");
this.$modal
.confirm(this.$t("systemRole.message.confirmStatusChange", { action: text, roleName: row.roleName }))
.then(() => changeRoleStatus(row.roleId, row.status))
.then(() => {
this.$modal.msgSuccess(this.$t("systemRole.message.statusChangeSuccess", { action: text }));
})
.catch(() => {
row.status = row.status === "0" ? "1" : "0";
});
},
cancel() {
this.open = false;
this.reset();
},
cancelDataScope() {
this.openDataScope = false;
this.reset();
},
reset() {
if (this.$refs.menu !== undefined) {
this.$refs.menu.setCheckedKeys([]);
}
this.menuExpand = false;
this.menuNodeAll = false;
this.deptExpand = true;
this.deptNodeAll = false;
this.form = {
roleId: undefined,
roleName: undefined,
roleKey: undefined,
roleSort: 0,
status: 0,
menuIds: [],
deptIds: [],
menuCheckStrictly: true,
deptCheckStrictly: true,
remark: undefined,
};
this.resetForm("form");
},
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
resetQuery() {
this.dateRange = [];
this.resetForm("queryForm");
this.handleQuery();
},
handleSelectionChange(selection) {
this.ids = selection.map((item) => item.roleId);
this.single = selection.length !== 1;
this.multiple = !selection.length;
},
handleCommand(command, row) {
switch (command) {
case "handleDataScope":
this.handleDataScope(row);
break;
case "handleAuthUser":
this.handleAuthUser(row);
break;
default:
break;
}
},
handleCheckedTreeExpand(value, type) {
if (type === "menu") {
const treeList = this.menuOptions;
for (let i = 0; i < treeList.length; i += 1) {
this.$refs.menu.store.nodesMap[treeList[i].id].expanded = value;
}
} else if (type === "dept") {
const treeList = this.deptOptions;
for (let i = 0; i < treeList.length; i += 1) {
this.$refs.dept.store.nodesMap[treeList[i].id].expanded = value;
}
}
},
created() {
this.getList()
handleCheckedTreeNodeAll(value, type) {
if (type === "menu") {
this.$refs.menu.setCheckedNodes(value ? this.menuOptions : []);
} else if (type === "dept") {
this.$refs.dept.setCheckedNodes(value ? this.deptOptions : []);
}
},
methods: {
/** 查询角色列表 */
getList() {
this.loading = true
listRole(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
this.roleList = response.data.list
this.total = response.data.total
this.loading = false
})
},
/** 查询菜单树结构 */
getMenuTreeselect() {
menuTreeselect().then(response => {
this.menuOptions = response.data
})
},
//
getMenuAllCheckedKeys() {
//
let checkedKeys = this.$refs.menu.getCheckedKeys()
//
let halfCheckedKeys = this.$refs.menu.getHalfCheckedKeys()
checkedKeys.unshift.apply(checkedKeys, halfCheckedKeys)
return checkedKeys
},
//
getDeptAllCheckedKeys() {
//
let checkedKeys = this.$refs.dept.getCheckedKeys()
//
let halfCheckedKeys = this.$refs.dept.getHalfCheckedKeys()
checkedKeys.unshift.apply(checkedKeys, halfCheckedKeys)
return checkedKeys
},
/** 根据角色ID查询菜单树结构 */
getRoleMenuTreeselect(roleId) {
return roleMenuTreeselect(roleId).then(response => {
this.menuOptions = response.menus
return response
})
},
/** 根据角色ID查询部门树结构 */
getDeptTree(roleId) {
return deptTreeSelect(roleId).then(response => {
this.deptOptions = response.depts
return response
})
},
//
handleStatusChange(row) {
let text = row.status === "0" ? "启用" : "停用"
this.$modal.confirm('确认要"' + text + '""' + row.roleName + '"角色吗?').then(function () {
return changeRoleStatus(row.roleId, row.status)
}).then(() => {
this.$modal.msgSuccess(text + "成功")
}).catch(function () {
row.status = row.status === "0" ? "1" : "0"
})
},
//
cancel() {
this.open = false
this.reset()
},
//
cancelDataScope() {
this.openDataScope = false
this.reset()
},
//
reset() {
if (this.$refs.menu != undefined) {
this.$refs.menu.setCheckedKeys([])
}
this.menuExpand = false
this.menuNodeAll = false
this.deptExpand = true
this.deptNodeAll = false
this.form = {
roleId: undefined,
roleName: undefined,
roleKey: undefined,
roleSort: 0,
status: 0,
menuIds: [],
deptIds: [],
menuCheckStrictly: true,
deptCheckStrictly: true,
remark: undefined
}
this.resetForm("form")
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
/** 重置按钮操作 */
resetQuery() {
this.dateRange = []
this.resetForm("queryForm")
this.handleQuery()
},
//
handleSelectionChange(selection) {
this.ids = selection.map(item => item.roleId)
this.single = selection.length != 1
this.multiple = !selection.length
},
//
handleCommand(command, row) {
switch (command) {
case "handleDataScope":
this.handleDataScope(row)
break
case "handleAuthUser":
this.handleAuthUser(row)
break
default:
break
}
},
// /
handleCheckedTreeExpand(value, type) {
if (type == 'menu') {
let treeList = this.menuOptions
for (let i = 0; i < treeList.length; i++) {
this.$refs.menu.store.nodesMap[treeList[i].id].expanded = value
}
} else if (type == 'dept') {
let treeList = this.deptOptions
for (let i = 0; i < treeList.length; i++) {
this.$refs.dept.store.nodesMap[treeList[i].id].expanded = value
}
}
},
// /
handleCheckedTreeNodeAll(value, type) {
if (type == 'menu') {
this.$refs.menu.setCheckedNodes(value ? this.menuOptions : [])
} else if (type == 'dept') {
this.$refs.dept.setCheckedNodes(value ? this.deptOptions : [])
}
},
//
handleCheckedTreeConnect(value, type) {
if (type == 'menu') {
this.form.menuCheckStrictly = value ? true : false
} else if (type == 'dept') {
this.form.deptCheckStrictly = value ? true : false
}
},
/** 新增按钮操作 */
handleAdd() {
this.reset()
this.getMenuTreeselect()
this.open = true
this.title = "添加角色"
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset()
const roleId = row.roleId || this.ids
const roleMenu = this.getRoleMenuTreeselect(roleId)
getRole(roleId).then(response => {
this.form = response.data
this.open = true
this.$nextTick(() => {
roleMenu.then(res => {
let checkedKeys = res.checkedKeys
checkedKeys.forEach((v) => {
this.$nextTick(() => {
this.$refs.menu.setChecked(v, true, false)
})
})
})
})
})
this.title = "修改角色"
},
/** 选择角色权限范围触发 */
dataScopeSelectChange(value) {
if (value !== '2') {
this.$refs.dept.setCheckedKeys([])
}
},
/** 分配数据权限操作 */
handleDataScope(row) {
this.reset()
const deptTreeSelect = this.getDeptTree(row.roleId)
getRole(row.roleId).then(response => {
this.form = response.data
this.openDataScope = true
this.$nextTick(() => {
deptTreeSelect.then(res => {
this.$refs.dept.setCheckedKeys(res.checkedKeys)
})
})
})
this.title = "分配数据权限"
},
/** 分配用户操作 */
handleAuthUser: function (row) {
const roleId = row.roleId
this.$router.push("/system/role-auth/user/" + roleId)
},
/** 提交按钮 */
submitForm: function () {
this.$refs["form"].validate(valid => {
if (valid) {
if (this.form.roleId != undefined) {
this.form.menuIds = this.getMenuAllCheckedKeys()
updateRole(this.form).then(response => {
this.$modal.msgSuccess("修改成功")
this.open = false
this.getList()
})
} else {
this.form.menuIds = this.getMenuAllCheckedKeys()
addRole(this.form).then(response => {
this.$modal.msgSuccess("新增成功")
this.open = false
this.getList()
})
}
}
})
},
/** 提交按钮(数据权限) */
submitDataScope: function () {
if (this.form.roleId != undefined) {
this.form.deptIds = this.getDeptAllCheckedKeys()
dataScope(this.form).then(response => {
this.$modal.msgSuccess("修改成功")
this.openDataScope = false
this.getList()
})
}
},
/** 删除按钮操作 */
handleDelete(row) {
const roleIds = row.roleId || this.ids
this.$modal.confirm('是否确认删除角色编号为"' + roleIds + '"的数据项?').then(function () {
return delRole(roleIds)
}).then(() => {
this.getList()
this.$modal.msgSuccess("删除成功")
}).catch(() => {})
},
/** 导出按钮操作 */
handleExport() {
this.download('system/role/export', {
...this.queryParams
}, `role_${new Date().getTime()}.xlsx`)
handleCheckedTreeConnect(value, type) {
if (type === "menu") {
this.form.menuCheckStrictly = !!value;
} else if (type === "dept") {
this.form.deptCheckStrictly = !!value;
}
},
handleAdd() {
this.reset();
this.getMenuTreeselect();
this.open = true;
this.title = this.$t("systemRole.dialog.addTitle");
},
handleUpdate(row) {
this.reset();
const roleId = row.roleId || this.ids;
const roleMenu = this.getRoleMenuTreeselect(roleId);
getRole(roleId).then((response) => {
this.form = response.data;
this.open = true;
this.$nextTick(() => {
roleMenu.then((res) => {
const checkedKeys = res.checkedKeys;
checkedKeys.forEach((value) => {
this.$nextTick(() => {
this.$refs.menu.setChecked(value, true, false);
});
});
});
});
});
this.title = this.$t("systemRole.dialog.editTitle");
},
dataScopeSelectChange(value) {
if (value !== "2") {
this.$refs.dept.setCheckedKeys([]);
}
},
handleDataScope(row) {
this.reset();
const deptTree = this.getDeptTree(row.roleId);
getRole(row.roleId).then((response) => {
this.form = response.data;
this.openDataScope = true;
this.$nextTick(() => {
deptTree.then((res) => {
this.$refs.dept.setCheckedKeys(res.checkedKeys);
});
});
});
this.title = this.$t("systemRole.dialog.dataScopeTitle");
},
handleAuthUser(row) {
this.$router.push("/system/role-auth/user/" + row.roleId);
},
submitForm() {
this.$refs.form.validate((valid) => {
if (!valid) {
return;
}
}
}
this.form.menuIds = this.getMenuAllCheckedKeys();
const request = this.form.roleId !== undefined ? updateRole(this.form) : addRole(this.form);
request.then(() => {
this.$modal.msgSuccess(
this.form.roleId !== undefined ? this.$t("systemRole.message.editSuccess") : this.$t("systemRole.message.addSuccess")
);
this.open = false;
this.getList();
});
});
},
submitDataScope() {
if (this.form.roleId !== undefined) {
this.form.deptIds = this.getDeptAllCheckedKeys();
dataScope(this.form).then(() => {
this.$modal.msgSuccess(this.$t("systemRole.message.editSuccess"));
this.openDataScope = false;
this.getList();
});
}
},
handleDelete(row) {
const roleIds = row.roleId || this.ids;
this.$modal
.confirm(this.$t("systemRole.message.confirmDelete", { roleIds }))
.then(() => delRole(roleIds))
.then(() => {
this.getList();
this.$modal.msgSuccess(this.$t("systemRole.message.deleteSuccess"));
})
.catch(() => {});
},
handleExport() {
this.download(
"system/role/export",
{
...this.queryParams,
},
`role_${new Date().getTime()}.xlsx`
);
},
},
};
</script>

100
src/views/system/user/components/ProfileSettingsCard.vue

@ -1,11 +1,11 @@
<template>
<el-card class="profile-settings-card" shadow="never">
<div slot="header" class="clearfix">
<span>基本资料</span>
<span>{{ $t("profile.title") }}</span>
</div>
<el-tabs v-model="activeTab">
<el-tab-pane label="基本资料" name="basic">
<el-tab-pane :label="$t('profile.tabs.basic')" name="basic">
<el-form
ref="profileForm"
:model="profileForm"
@ -15,12 +15,12 @@
>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="登录账户">
<el-form-item :label="$t('profile.form.account')">
<el-input v-model="profileForm.account" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="所属企业">
<el-form-item :label="$t('profile.form.businessName')">
<el-input v-model="profileForm.businessName" disabled />
</el-form-item>
</el-col>
@ -28,16 +28,16 @@
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="昵称" prop="nickName">
<el-form-item :label="$t('profile.form.nickName')" prop="nickName">
<el-input v-model="profileForm.nickName" maxlength="30" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="谷歌地图 Key">
<el-form-item :label="$t('profile.form.googleKey')">
<el-input
v-model="profileForm.googleKey"
:disabled="!profileForm.canEditBusinessConfig"
placeholder="请输入谷歌地图 Key"
:placeholder="$t('profile.placeholder.googleKey')"
/>
</el-form-item>
</el-col>
@ -45,20 +45,20 @@
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="高德地图 Key">
<el-form-item :label="$t('profile.form.gaodeKey')">
<el-input
v-model="profileForm.gaodeKey"
:disabled="!profileForm.canEditBusinessConfig"
placeholder="请输入高德地图 Key"
:placeholder="$t('profile.placeholder.gaodeKey')"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="高德安全密钥">
<el-form-item :label="$t('profile.form.gaodeSecurityKey')">
<el-input
v-model="profileForm.gaodeSecurityKey"
:disabled="!profileForm.canEditBusinessConfig"
placeholder="请输入高德安全密钥"
:placeholder="$t('profile.placeholder.gaodeSecurityKey')"
/>
</el-form-item>
</el-col>
@ -69,20 +69,20 @@
type="info"
:closable="false"
show-icon
title="当前账号只能修改自己的昵称,企业地图 Key 仅企业管理员可修改。"
:title="$t('profile.tip.businessConfigReadonly')"
class="profile-tip"
/>
<el-form-item>
<el-button type="primary" :loading="profileSaving" @click="submitProfile">
保存资料
{{ $t("profile.button.saveProfile") }}
</el-button>
<el-button @click="loadProfile">重置</el-button>
<el-button @click="loadProfile">{{ $t("profile.button.reset") }}</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="修改密码" name="password">
<el-tab-pane :label="$t('profile.tabs.password')" name="password">
<el-form
ref="passwordForm"
:model="passwordForm"
@ -90,35 +90,35 @@
label-width="120px"
size="small"
>
<el-form-item label="旧密码" prop="oldPassword">
<el-form-item :label="$t('profile.form.oldPassword')" prop="oldPassword">
<el-input
v-model="passwordForm.oldPassword"
type="password"
show-password
placeholder="请输入旧密码"
:placeholder="$t('profile.placeholder.oldPassword')"
/>
</el-form-item>
<el-form-item label="新密码" prop="newPassword">
<el-form-item :label="$t('profile.form.newPassword')" prop="newPassword">
<el-input
v-model="passwordForm.newPassword"
type="password"
show-password
placeholder="请输入新密码"
:placeholder="$t('profile.placeholder.newPassword')"
/>
</el-form-item>
<el-form-item label="确认密码" prop="confirmPassword">
<el-form-item :label="$t('profile.form.confirmPassword')" prop="confirmPassword">
<el-input
v-model="passwordForm.confirmPassword"
type="password"
show-password
placeholder="请再次输入新密码"
:placeholder="$t('profile.placeholder.confirmPassword')"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="passwordSaving" @click="submitPassword">
修改密码
{{ $t("profile.button.changePassword") }}
</el-button>
<el-button @click="resetPasswordForm">重置</el-button>
<el-button @click="resetPasswordForm">{{ $t("profile.button.reset") }}</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
@ -158,44 +158,48 @@ function createDefaultPasswordForm() {
export default {
name: "ProfileSettingsCard",
data() {
const confirmPasswordValidator = (rule, value, callback) => {
if (value !== this.passwordForm.newPassword) {
callback(new Error("两次输入的新密码不一致"));
return;
}
callback();
};
return {
activeTab: "basic",
profileSaving: false,
passwordSaving: false,
profileForm: createDefaultProfileForm(),
passwordForm: createDefaultPasswordForm(),
profileRules: {
profileRules: {},
passwordRules: {},
};
},
created() {
this.initI18nState();
this.loadProfile();
},
methods: {
initI18nState() {
const confirmPasswordValidator = (rule, value, callback) => {
if (value !== this.passwordForm.newPassword) {
callback(new Error(this.$t("profile.validation.confirmPasswordMismatch")));
return;
}
callback();
};
this.profileRules = {
nickName: [
{ required: true, message: "昵称不能为空", trigger: "blur" },
{ required: true, message: this.$t("profile.validation.nickNameRequired"), trigger: "blur" },
],
},
passwordRules: {
};
this.passwordRules = {
oldPassword: [
{ required: true, message: "旧密码不能为空", trigger: "blur" },
{ required: true, message: this.$t("profile.validation.oldPasswordRequired"), trigger: "blur" },
],
newPassword: [
{ required: true, message: "新密码不能为空", trigger: "blur" },
{ min: 6, max: 20, message: "长度在 6 到 20 个字符", trigger: "blur" },
{ required: true, message: this.$t("profile.validation.newPasswordRequired"), trigger: "blur" },
{ min: 6, max: 20, message: this.$t("profile.validation.newPasswordLength"), trigger: "blur" },
],
confirmPassword: [
{ required: true, message: "确认密码不能为空", trigger: "blur" },
{ required: true, message: this.$t("profile.validation.confirmPasswordRequired"), trigger: "blur" },
{ validator: confirmPasswordValidator, trigger: "blur" },
],
},
};
},
created() {
this.loadProfile();
},
methods: {
};
},
loadProfile() {
getUserProfile().then((response) => {
const data = response && response.data ? response.data : {};
@ -216,7 +220,7 @@ export default {
this.profileSaving = true;
updateUserProfile(payload)
.then(() => {
this.$modal.msgSuccess("基本资料已更新");
this.$modal.msgSuccess(this.$t("profile.message.profileUpdated"));
this.loadProfile();
})
.finally(() => {
@ -232,7 +236,7 @@ export default {
this.passwordSaving = true;
updateUserPwd(this.passwordForm.oldPassword, this.passwordForm.newPassword)
.then(() => {
this.$modal.msgSuccess("密码修改成功");
this.$modal.msgSuccess(this.$t("profile.message.passwordUpdated"));
this.resetPasswordForm();
})
.finally(() => {

881
src/views/system/user/index.vue

File diff suppressed because it is too large
Loading…
Cancel
Save