项目初始化
This commit is contained in:
97
web/src/views/backend/auth/admin/index.vue
Normal file
97
web/src/views/backend/auth/admin/index.vue
Normal file
@@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<div class="default-main ba-table-box">
|
||||
<el-alert class="ba-table-alert" v-if="baTable.table.remark" :title="baTable.table.remark" type="info" show-icon />
|
||||
|
||||
<!-- 表格顶部菜单 -->
|
||||
<TableHeader
|
||||
:buttons="['refresh', 'add', 'edit', 'delete', 'comSearch', 'quickSearch', 'columnDisplay']"
|
||||
:quick-search-placeholder="t('Quick search placeholder', { fields: t('auth.admin.username') + '/' + t('auth.admin.nickname') })"
|
||||
/>
|
||||
|
||||
<!-- 表格 -->
|
||||
<!-- 要使用`el-table`组件原有的属性,直接加在Table标签上即可 -->
|
||||
<Table />
|
||||
|
||||
<!-- 表单 -->
|
||||
<PopupForm />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { provide } from 'vue'
|
||||
import baTableClass from '/@/utils/baTable'
|
||||
import PopupForm from './popupForm.vue'
|
||||
import Table from '/@/components/table/index.vue'
|
||||
import TableHeader from '/@/components/table/header/index.vue'
|
||||
import { defaultOptButtons } from '/@/components/table'
|
||||
import { baTableApi } from '/@/api/common'
|
||||
import { useAdminInfo } from '/@/stores/adminInfo'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
defineOptions({
|
||||
name: 'auth/admin',
|
||||
})
|
||||
|
||||
const { t } = useI18n()
|
||||
const adminInfo = useAdminInfo()
|
||||
|
||||
const optButtons = defaultOptButtons(['edit', 'delete'])
|
||||
optButtons[1].display = (row) => {
|
||||
return row.id != adminInfo.id
|
||||
}
|
||||
|
||||
const baTable = new baTableClass(
|
||||
new baTableApi('/admin/auth.Admin/'),
|
||||
{
|
||||
column: [
|
||||
{ type: 'selection', align: 'center', operator: false },
|
||||
{ label: t('Id'), prop: 'id', align: 'center', operator: '=', operatorPlaceholder: t('Id'), width: 70 },
|
||||
{ label: t('auth.admin.username'), prop: 'username', align: 'center', operator: 'LIKE', operatorPlaceholder: t('Fuzzy query') },
|
||||
{ label: t('auth.admin.nickname'), prop: 'nickname', align: 'center', operator: 'LIKE', operatorPlaceholder: t('Fuzzy query') },
|
||||
{ label: t('auth.admin.group'), prop: 'group_name_arr', align: 'center', operator: false, render: 'tags' },
|
||||
{ label: t('auth.admin.avatar'), prop: 'avatar', align: 'center', render: 'image', operator: false },
|
||||
{ label: t('auth.admin.email'), prop: 'email', align: 'center', operator: 'LIKE', operatorPlaceholder: t('Fuzzy query') },
|
||||
{ label: t('auth.admin.mobile'), prop: 'mobile', align: 'center', operator: 'LIKE', operatorPlaceholder: t('Fuzzy query') },
|
||||
{
|
||||
label: t('auth.admin.Last login'),
|
||||
prop: 'last_login_time',
|
||||
align: 'center',
|
||||
render: 'datetime',
|
||||
sortable: 'custom',
|
||||
operator: 'RANGE',
|
||||
width: 160,
|
||||
},
|
||||
{ label: t('Create time'), prop: 'create_time', align: 'center', render: 'datetime', sortable: 'custom', operator: 'RANGE', width: 160 },
|
||||
{
|
||||
label: t('State'),
|
||||
prop: 'status',
|
||||
align: 'center',
|
||||
render: 'tag',
|
||||
custom: { disable: 'danger', enable: 'success' },
|
||||
replaceValue: { disable: t('Disable'), enable: t('Enable') },
|
||||
},
|
||||
{
|
||||
label: t('Operate'),
|
||||
align: 'center',
|
||||
width: '100',
|
||||
render: 'buttons',
|
||||
buttons: optButtons,
|
||||
operator: false,
|
||||
},
|
||||
],
|
||||
dblClickNotEditColumn: [undefined, 'status'],
|
||||
},
|
||||
{
|
||||
defaultItems: {
|
||||
status: 'enable',
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
provide('baTable', baTable)
|
||||
|
||||
baTable.mount()
|
||||
baTable.getData()
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
198
web/src/views/backend/auth/admin/popupForm.vue
Normal file
198
web/src/views/backend/auth/admin/popupForm.vue
Normal file
@@ -0,0 +1,198 @@
|
||||
<template>
|
||||
<!-- 对话框表单 -->
|
||||
<el-dialog
|
||||
class="ba-operate-dialog"
|
||||
:close-on-click-modal="false"
|
||||
:model-value="['Add', 'Edit'].includes(baTable.form.operate!)"
|
||||
@close="baTable.toggleForm"
|
||||
:destroy-on-close="true"
|
||||
>
|
||||
<template #header>
|
||||
<div class="title" v-drag="['.ba-operate-dialog', '.el-dialog__header']" v-zoom="'.ba-operate-dialog'">
|
||||
{{ baTable.form.operate ? t(baTable.form.operate) : '' }}
|
||||
</div>
|
||||
</template>
|
||||
<el-scrollbar v-loading="baTable.form.loading" class="ba-table-form-scrollbar">
|
||||
<div
|
||||
class="ba-operate-form"
|
||||
:class="'ba-' + baTable.form.operate + '-form'"
|
||||
:style="config.layout.shrink ? '' : 'width: calc(100% - ' + baTable.form.labelWidth! / 2 + 'px)'"
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
@keyup.enter="baTable.onSubmit(formRef)"
|
||||
:model="baTable.form.items"
|
||||
:label-position="config.layout.shrink ? 'top' : 'right'"
|
||||
:label-width="baTable.form.labelWidth + 'px'"
|
||||
:rules="rules"
|
||||
v-if="!baTable.form.loading"
|
||||
>
|
||||
<FormItem
|
||||
:label="t('auth.admin.username')"
|
||||
v-model="baTable.form.items!.username"
|
||||
type="string"
|
||||
prop="username"
|
||||
:placeholder="t('auth.admin.Administrator login')"
|
||||
/>
|
||||
<FormItem
|
||||
:label="t('auth.admin.nickname')"
|
||||
v-model="baTable.form.items!.nickname"
|
||||
type="string"
|
||||
prop="nickname"
|
||||
:placeholder="t('Please input field', { field: t('auth.admin.nickname') })"
|
||||
/>
|
||||
<FormItem
|
||||
:label="t('auth.admin.group')"
|
||||
v-model="baTable.form.items!.group_arr"
|
||||
prop="group_arr"
|
||||
type="remoteSelect"
|
||||
:key="'group-' + baTable.form.items!.id"
|
||||
:input-attr="{
|
||||
multiple: true,
|
||||
params: { isTree: true, absoluteAuth: adminInfo.id == baTable.form.items!.id ? 0 : 1 },
|
||||
field: 'name',
|
||||
remoteUrl: '/admin/auth.Group/index',
|
||||
placeholder: t('Click select'),
|
||||
}"
|
||||
/>
|
||||
<FormItem :label="t('auth.admin.avatar')" type="image" v-model="baTable.form.items!.avatar" />
|
||||
<FormItem
|
||||
:label="t('auth.admin.email')"
|
||||
prop="email"
|
||||
v-model="baTable.form.items!.email"
|
||||
type="string"
|
||||
:placeholder="t('Please input field', { field: t('auth.admin.email') })"
|
||||
/>
|
||||
<FormItem
|
||||
:label="t('auth.admin.mobile')"
|
||||
prop="mobile"
|
||||
v-model="baTable.form.items!.mobile"
|
||||
type="string"
|
||||
:placeholder="t('Please input field', { field: t('auth.admin.mobile') })"
|
||||
/>
|
||||
<FormItem
|
||||
:label="t('auth.admin.Password')"
|
||||
prop="password"
|
||||
v-model="baTable.form.items!.password"
|
||||
type="password"
|
||||
:input-attr="{ autocomplete: 'new-password' }"
|
||||
:placeholder="
|
||||
baTable.form.operate == 'Add'
|
||||
? t('Please input field', { field: t('auth.admin.Password') })
|
||||
: t('auth.admin.Please leave blank if not modified')
|
||||
"
|
||||
/>
|
||||
<el-form-item prop="motto" :label="t('auth.admin.Personal signature')">
|
||||
<el-input
|
||||
@keyup.enter.stop=""
|
||||
@keyup.ctrl.enter="baTable.onSubmit(formRef)"
|
||||
v-model="baTable.form.items!.motto"
|
||||
type="textarea"
|
||||
:placeholder="t('Please input field', { field: t('auth.admin.Personal signature') })"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<FormItem
|
||||
:label="t('State')"
|
||||
v-model="baTable.form.items!.status"
|
||||
type="radio"
|
||||
:input-attr="{
|
||||
border: true,
|
||||
content: { disable: t('Disable'), enable: t('Enable') },
|
||||
}"
|
||||
/>
|
||||
</el-form>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
<template #footer>
|
||||
<div :style="'width: calc(100% - ' + baTable.form.labelWidth! / 1.8 + 'px)'">
|
||||
<el-button @click="baTable.toggleForm('')">{{ t('Cancel') }}</el-button>
|
||||
<el-button v-blur :loading="baTable.form.submitLoading" @click="baTable.onSubmit(formRef)" type="primary">
|
||||
{{ baTable.form.operateIds && baTable.form.operateIds.length > 1 ? t('Save and edit next item') : t('Save') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, inject, watch, useTemplateRef } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import type baTableClass from '/@/utils/baTable'
|
||||
import { regularPassword, buildValidatorData } from '/@/utils/validate'
|
||||
import type { FormItemRule } from 'element-plus'
|
||||
import FormItem from '/@/components/formItem/index.vue'
|
||||
import { useAdminInfo } from '/@/stores/adminInfo'
|
||||
import { useConfig } from '/@/stores/config'
|
||||
|
||||
const config = useConfig()
|
||||
const adminInfo = useAdminInfo()
|
||||
const formRef = useTemplateRef('formRef')
|
||||
const baTable = inject('baTable') as baTableClass
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const rules: Partial<Record<string, FormItemRule[]>> = reactive({
|
||||
username: [buildValidatorData({ name: 'required', title: t('auth.admin.username') }), buildValidatorData({ name: 'account' })],
|
||||
nickname: [buildValidatorData({ name: 'required', title: t('auth.admin.nickname') })],
|
||||
group_arr: [buildValidatorData({ name: 'required', message: t('Please select field', { field: t('auth.admin.group') }) })],
|
||||
email: [buildValidatorData({ name: 'email', message: t('Please enter the correct field', { field: t('auth.admin.email') }) })],
|
||||
mobile: [buildValidatorData({ name: 'mobile', message: t('Please enter the correct field', { field: t('auth.admin.mobile') }) })],
|
||||
password: [
|
||||
{
|
||||
validator: (rule: any, val: string, callback: Function) => {
|
||||
if (baTable.form.operate == 'Add') {
|
||||
if (!val) {
|
||||
return callback(new Error(t('Please input field', { field: t('auth.admin.Password') })))
|
||||
}
|
||||
} else {
|
||||
if (!val) {
|
||||
return callback()
|
||||
}
|
||||
}
|
||||
if (!regularPassword(val)) {
|
||||
return callback(new Error(t('validate.Please enter the correct password')))
|
||||
}
|
||||
return callback()
|
||||
},
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
watch(
|
||||
() => baTable.form.operate,
|
||||
(newVal) => {
|
||||
rules.password![0].required = newVal == 'Add'
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.avatar-uploader {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
border-radius: var(--el-border-radius-small);
|
||||
box-shadow: var(--el-box-shadow-light);
|
||||
border: 1px dashed var(--el-border-color);
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
width: 110px;
|
||||
height: 110px;
|
||||
}
|
||||
.avatar-uploader:hover {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
.avatar {
|
||||
width: 110px;
|
||||
height: 110px;
|
||||
display: block;
|
||||
}
|
||||
.image-slot {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
145
web/src/views/backend/auth/adminLog/index.vue
Normal file
145
web/src/views/backend/auth/adminLog/index.vue
Normal file
@@ -0,0 +1,145 @@
|
||||
<template>
|
||||
<div class="default-main ba-table-box">
|
||||
<el-alert class="ba-table-alert" v-if="baTable.table.remark" :title="baTable.table.remark" type="info" show-icon />
|
||||
|
||||
<!-- 表格顶部菜单 -->
|
||||
<TableHeader
|
||||
:buttons="['refresh', 'delete', 'comSearch', 'quickSearch', 'columnDisplay']"
|
||||
:quick-search-placeholder="t('Quick search placeholder', { fields: t('auth.adminLog.title') })"
|
||||
/>
|
||||
<!-- 表格 -->
|
||||
<!-- 要使用`el-table`组件原有的属性,直接加在Table标签上即可 -->
|
||||
<Table />
|
||||
|
||||
<Info />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { concat, cloneDeep } from 'lodash-es'
|
||||
import { provide } from 'vue'
|
||||
import baTableClass from '/@/utils/baTable'
|
||||
import Table from '/@/components/table/index.vue'
|
||||
import TableHeader from '/@/components/table/header/index.vue'
|
||||
import { defaultOptButtons } from '/@/components/table'
|
||||
import { baTableApi } from '/@/api/common'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import Info from './info.vue'
|
||||
import { buildJsonToElTreeData } from '/@/utils/common'
|
||||
|
||||
defineOptions({
|
||||
name: 'auth/adminLog',
|
||||
})
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
let optButtons: OptButton[] = [
|
||||
{
|
||||
render: 'tipButton',
|
||||
name: 'info',
|
||||
title: 'Info',
|
||||
text: '',
|
||||
type: 'primary',
|
||||
icon: 'fa fa-search-plus',
|
||||
class: 'table-row-edit',
|
||||
disabledTip: false,
|
||||
click: (row: TableRow) => {
|
||||
infoButtonClick(row)
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
optButtons = concat(optButtons, defaultOptButtons(['delete']))
|
||||
|
||||
const baTable = new baTableClass(new baTableApi('/admin/auth.AdminLog/'), {
|
||||
column: [
|
||||
{ type: 'selection', align: 'center', operator: false },
|
||||
{ label: t('Id'), prop: 'id', align: 'center', operator: '=', operatorPlaceholder: t('Id'), width: 70 },
|
||||
{
|
||||
label: t('auth.adminLog.admin_id'),
|
||||
prop: 'admin_id',
|
||||
align: 'center',
|
||||
operator: 'LIKE',
|
||||
operatorPlaceholder: t('Fuzzy query'),
|
||||
width: 70,
|
||||
},
|
||||
{
|
||||
label: t('auth.adminLog.username'),
|
||||
prop: 'username',
|
||||
align: 'center',
|
||||
operator: 'LIKE',
|
||||
operatorPlaceholder: t('Fuzzy query'),
|
||||
width: 160,
|
||||
},
|
||||
{ label: t('auth.adminLog.title'), prop: 'title', align: 'center', operator: 'LIKE', operatorPlaceholder: t('Fuzzy query') },
|
||||
{
|
||||
show: false,
|
||||
label: t('auth.adminLog.data'),
|
||||
prop: 'data',
|
||||
align: 'center',
|
||||
operator: 'LIKE',
|
||||
operatorPlaceholder: t('Fuzzy query'),
|
||||
showOverflowTooltip: true,
|
||||
},
|
||||
{
|
||||
label: t('auth.adminLog.url'),
|
||||
prop: 'url',
|
||||
align: 'center',
|
||||
operator: 'LIKE',
|
||||
operatorPlaceholder: t('Fuzzy query'),
|
||||
showOverflowTooltip: true,
|
||||
render: 'url',
|
||||
},
|
||||
{ label: t('auth.adminLog.ip'), prop: 'ip', align: 'center', operator: 'LIKE', operatorPlaceholder: t('Fuzzy query'), render: 'tag' },
|
||||
{
|
||||
label: t('auth.adminLog.useragent'),
|
||||
prop: 'useragent',
|
||||
align: 'center',
|
||||
operator: 'LIKE',
|
||||
operatorPlaceholder: t('Fuzzy query'),
|
||||
showOverflowTooltip: true,
|
||||
},
|
||||
{
|
||||
label: t('Create time'),
|
||||
prop: 'create_time',
|
||||
align: 'center',
|
||||
render: 'datetime',
|
||||
sortable: 'custom',
|
||||
operator: 'RANGE',
|
||||
width: 160,
|
||||
},
|
||||
{
|
||||
label: t('Operate'),
|
||||
align: 'center',
|
||||
width: '100',
|
||||
render: 'buttons',
|
||||
buttons: optButtons,
|
||||
operator: false,
|
||||
},
|
||||
],
|
||||
dblClickNotEditColumn: [undefined],
|
||||
})
|
||||
|
||||
// 利用双击单元格前钩子重写双击操作
|
||||
baTable.before.onTableDblclick = ({ row }) => {
|
||||
infoButtonClick(row)
|
||||
return false
|
||||
}
|
||||
|
||||
baTable.mount()
|
||||
baTable.getData()
|
||||
|
||||
provide('baTable', baTable)
|
||||
|
||||
/** 点击查看详情按钮响应 */
|
||||
const infoButtonClick = (row: TableRow) => {
|
||||
if (!row) return
|
||||
// 数据来自表格数据,未重新请求api,深克隆,不然可能会影响表格
|
||||
let rowClone = cloneDeep(row)
|
||||
rowClone.data = rowClone.data ? [{ label: '点击展开', children: buildJsonToElTreeData(JSON.parse(rowClone.data)) }] : []
|
||||
baTable.form.extend!['info'] = rowClone
|
||||
baTable.form.operate = 'Info'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
62
web/src/views/backend/auth/adminLog/info.vue
Normal file
62
web/src/views/backend/auth/adminLog/info.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<!-- 查看详情 -->
|
||||
<el-dialog class="ba-operate-dialog" :model-value="baTable.form.operate ? true : false" @close="baTable.toggleForm">
|
||||
<template #header>
|
||||
<div class="title" v-drag="['.ba-operate-dialog', '.el-dialog__header']" v-zoom="'.ba-operate-dialog'">{{ t('Info') }}</div>
|
||||
</template>
|
||||
<el-scrollbar v-loading="baTable.form.loading" class="ba-table-form-scrollbar">
|
||||
<div class="ba-operate-form" :class="'ba-' + baTable.form.operate + '-form'">
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item :label="t('Id')">
|
||||
{{ baTable.form.extend!.info.id }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item :label="t('auth.adminLog.Operation administrator')">
|
||||
{{ baTable.form.extend!.info.username }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item :label="t('auth.adminLog.title')">
|
||||
{{ baTable.form.extend!.info.title }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item :label="t('auth.adminLog.Operator IP')">
|
||||
{{ baTable.form.extend!.info.ip }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item :width="120" :span="2" label="URL">
|
||||
{{ baTable.form.extend!.info.url }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item :width="120" :span="2" label="User Agent">
|
||||
{{ baTable.form.extend!.info.useragent }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item :width="120" :span="2" :label="t('Create time')">
|
||||
{{ timeFormat(baTable.form.extend!.info.create_time) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item :width="120" :span="2" :label="t('auth.adminLog.Request data')">
|
||||
<el-tree class="table-el-tree" :data="baTable.form.extend!.info.data" :props="{ label: 'label', children: 'children' }" />
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { inject } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import type BaTable from '/@/utils/baTable'
|
||||
import { timeFormat } from '/@/utils/common'
|
||||
|
||||
const baTable = inject('baTable') as BaTable
|
||||
|
||||
const { t } = useI18n()
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.table-el-tree {
|
||||
:deep(.el-tree-node) {
|
||||
white-space: unset;
|
||||
}
|
||||
:deep(.el-tree-node__content) {
|
||||
display: block;
|
||||
align-items: unset;
|
||||
height: unset;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
179
web/src/views/backend/auth/group/index.vue
Normal file
179
web/src/views/backend/auth/group/index.vue
Normal file
@@ -0,0 +1,179 @@
|
||||
<template>
|
||||
<div class="default-main ba-table-box">
|
||||
<el-alert
|
||||
class="ba-table-alert group-super-alert"
|
||||
v-if="!adminInfo.super"
|
||||
:title="t('auth.group.Manage subordinate role groups here')"
|
||||
type="info"
|
||||
show-icon
|
||||
/>
|
||||
<el-alert class="ba-table-alert" v-if="baTable.table.remark" :title="baTable.table.remark" type="info" show-icon />
|
||||
|
||||
<!-- 表格顶部菜单 -->
|
||||
<TableHeader
|
||||
:buttons="['refresh', 'add', 'edit', 'delete', 'unfold', 'quickSearch', 'columnDisplay']"
|
||||
:quick-search-placeholder="t('Quick search placeholder', { fields: t('auth.group.GroupName') })"
|
||||
/>
|
||||
|
||||
<!-- 表格 -->
|
||||
<!-- 要使用`el-table`组件原有的属性,直接加在Table标签上即可 -->
|
||||
<Table ref="tableRef" :pagination="false" />
|
||||
|
||||
<!-- 表单 -->
|
||||
<PopupForm ref="formRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, provide, useTemplateRef } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import PopupForm from './popupForm.vue'
|
||||
import { getAdminRules } from '/@/api/backend/auth/group'
|
||||
import { baTableApi } from '/@/api/common'
|
||||
import { defaultOptButtons } from '/@/components/table'
|
||||
import TableHeader from '/@/components/table/header/index.vue'
|
||||
import Table from '/@/components/table/index.vue'
|
||||
import { useAdminInfo } from '/@/stores/adminInfo'
|
||||
import baTableClass from '/@/utils/baTable'
|
||||
import { getArrayKey } from '/@/utils/common'
|
||||
import { uuid } from '/@/utils/random'
|
||||
|
||||
defineOptions({
|
||||
name: 'auth/group',
|
||||
})
|
||||
|
||||
const { t } = useI18n()
|
||||
const adminInfo = useAdminInfo()
|
||||
const formRef = useTemplateRef('formRef')
|
||||
const tableRef = useTemplateRef('tableRef')
|
||||
|
||||
const baTable: baTableClass = new baTableClass(
|
||||
new baTableApi('/admin/auth.Group/'),
|
||||
{
|
||||
expandAll: true,
|
||||
dblClickNotEditColumn: [undefined],
|
||||
column: [
|
||||
{ type: 'selection', align: 'center' },
|
||||
{ label: t('auth.group.Group name'), prop: 'name', align: 'left', width: '200' },
|
||||
{ label: t('auth.group.jurisdiction'), prop: 'rules', align: 'center' },
|
||||
{
|
||||
label: t('State'),
|
||||
prop: 'status',
|
||||
align: 'center',
|
||||
render: 'tag',
|
||||
custom: { 0: 'danger', 1: 'success' },
|
||||
replaceValue: { 0: t('Disable'), 1: t('Enable') },
|
||||
},
|
||||
{ label: t('Update time'), prop: 'update_time', align: 'center', width: '160', render: 'datetime' },
|
||||
{ label: t('Create time'), prop: 'create_time', align: 'center', width: '160', render: 'datetime' },
|
||||
{ label: t('Operate'), align: 'center', width: '130', render: 'buttons', buttons: defaultOptButtons(['edit', 'delete']) },
|
||||
],
|
||||
},
|
||||
{
|
||||
defaultItems: {
|
||||
status: 1,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// 利用提交前钩子重写提交操作
|
||||
baTable.before.onSubmit = ({ formEl, operate, items }) => {
|
||||
let submitCallback = () => {
|
||||
baTable.form.submitLoading = true
|
||||
baTable.api
|
||||
.postData(operate, {
|
||||
...items,
|
||||
rules: formRef.value?.getCheckeds(),
|
||||
})
|
||||
.then((res) => {
|
||||
baTable.onTableHeaderAction('refresh', {})
|
||||
baTable.form.submitLoading = false
|
||||
baTable.form.operateIds?.shift()
|
||||
if (baTable.form.operateIds!.length > 0) {
|
||||
baTable.toggleForm('Edit', baTable.form.operateIds)
|
||||
} else {
|
||||
baTable.toggleForm()
|
||||
}
|
||||
baTable.runAfter('onSubmit', { res })
|
||||
})
|
||||
.catch(() => {
|
||||
baTable.form.submitLoading = false
|
||||
})
|
||||
}
|
||||
|
||||
if (formEl) {
|
||||
baTable.form.ref = formEl
|
||||
formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
submitCallback()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
submitCallback()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 利用双击单元格前钩子重写双击操作
|
||||
baTable.before.onTableDblclick = ({ row }) => {
|
||||
return baTable.table.extend!.adminGroup.indexOf(row.id) === -1
|
||||
}
|
||||
|
||||
// 获取到数据后钩子
|
||||
baTable.after.getData = ({ res }) => {
|
||||
baTable.table.extend!.adminGroup = res.data.group
|
||||
let buttonsKey = getArrayKey(baTable.table.column, 'render', 'buttons')
|
||||
baTable.table.column[buttonsKey].buttons!.forEach((value: OptButton) => {
|
||||
value.display = (row) => {
|
||||
return res.data.group.indexOf(row.id) === -1
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 切换表单后钩子
|
||||
baTable.after.toggleForm = ({ operate }) => {
|
||||
if (operate == 'Add') {
|
||||
menuRuleTreeUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
// 编辑请求完成后钩子
|
||||
baTable.after.getEditData = () => {
|
||||
menuRuleTreeUpdate()
|
||||
}
|
||||
|
||||
const menuRuleTreeUpdate = () => {
|
||||
getAdminRules().then((res) => {
|
||||
baTable.form.extend!.menuRules = res.data.list
|
||||
|
||||
if (baTable.form.items!.rules && baTable.form.items!.rules.length) {
|
||||
if (baTable.form.items!.rules.includes('*')) {
|
||||
let arr: number[] = []
|
||||
for (const key in baTable.form.extend!.menuRules) {
|
||||
arr.push(baTable.form.extend!.menuRules[key].id)
|
||||
}
|
||||
baTable.form.extend!.defaultCheckedKeys = arr
|
||||
} else {
|
||||
baTable.form.extend!.defaultCheckedKeys = baTable.form.items!.rules
|
||||
}
|
||||
} else {
|
||||
baTable.form.extend!.defaultCheckedKeys = []
|
||||
}
|
||||
baTable.form.extend!.treeKey = uuid()
|
||||
})
|
||||
}
|
||||
|
||||
provide('baTable', baTable)
|
||||
|
||||
onMounted(() => {
|
||||
baTable.table.ref = tableRef.value
|
||||
baTable.mount()
|
||||
baTable.getData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.group-super-alert {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
173
web/src/views/backend/auth/group/popupForm.vue
Normal file
173
web/src/views/backend/auth/group/popupForm.vue
Normal file
@@ -0,0 +1,173 @@
|
||||
<template>
|
||||
<!-- 对话框表单 -->
|
||||
<el-dialog
|
||||
class="ba-operate-dialog"
|
||||
:close-on-click-modal="false"
|
||||
:model-value="['Add', 'Edit'].includes(baTable.form.operate!)"
|
||||
@close="baTable.toggleForm"
|
||||
:destroy-on-close="true"
|
||||
>
|
||||
<template #header>
|
||||
<div class="title" v-drag="['.ba-operate-dialog', '.el-dialog__header']" v-zoom="'.ba-operate-dialog'">
|
||||
{{ baTable.form.operate ? t(baTable.form.operate) : '' }}
|
||||
</div>
|
||||
</template>
|
||||
<el-scrollbar v-loading="baTable.form.loading" class="ba-table-form-scrollbar">
|
||||
<div
|
||||
class="ba-operate-form"
|
||||
:class="'ba-' + baTable.form.operate + '-form'"
|
||||
:style="config.layout.shrink ? '' : 'width: calc(100% - ' + baTable.form.labelWidth! / 2 + 'px)'"
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
@keyup.enter="baTable.onSubmit(formRef)"
|
||||
:model="baTable.form.items"
|
||||
:label-position="config.layout.shrink ? 'top' : 'right'"
|
||||
:label-width="baTable.form.labelWidth + 'px'"
|
||||
:rules="rules"
|
||||
v-if="!baTable.form.loading"
|
||||
>
|
||||
<FormItem
|
||||
:label="t('auth.group.Parent group')"
|
||||
v-model="baTable.form.items!.pid"
|
||||
type="remoteSelect"
|
||||
prop="pid"
|
||||
:input-attr="{
|
||||
params: { isTree: true },
|
||||
field: 'name',
|
||||
remoteUrl: baTable.api.actionUrl.get('index'),
|
||||
placeholder: t('Click select'),
|
||||
emptyValues: ['', null, undefined, 0],
|
||||
valueOnClear: 0,
|
||||
}"
|
||||
/>
|
||||
|
||||
<el-form-item prop="name" :label="t('auth.group.Group name')">
|
||||
<el-input
|
||||
v-model="baTable.form.items!.name"
|
||||
type="string"
|
||||
:placeholder="t('Please input field', { field: t('auth.group.Group name') })"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="auth" :label="t('auth.group.jurisdiction')">
|
||||
<el-tree
|
||||
ref="treeRef"
|
||||
:key="baTable.form.extend!.treeKey"
|
||||
:default-checked-keys="baTable.form.extend!.defaultCheckedKeys"
|
||||
:default-expand-all="true"
|
||||
show-checkbox
|
||||
node-key="id"
|
||||
:props="{ children: 'children', label: 'title', class: treeNodeClass }"
|
||||
:data="baTable.form.extend!.menuRules"
|
||||
class="w100"
|
||||
/>
|
||||
</el-form-item>
|
||||
<FormItem
|
||||
:label="t('State')"
|
||||
v-model="baTable.form.items!.status"
|
||||
type="radio"
|
||||
:input-attr="{
|
||||
border: true,
|
||||
content: { 0: t('Disable'), 1: t('Enable') },
|
||||
}"
|
||||
/>
|
||||
</el-form>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
<template #footer>
|
||||
<div :style="'width: calc(100% - ' + baTable.form.labelWidth! / 1.8 + 'px)'">
|
||||
<el-button @click="baTable.toggleForm('')">{{ t('Cancel') }}</el-button>
|
||||
<el-button v-blur :loading="baTable.form.submitLoading" @click="baTable.onSubmit(formRef)" type="primary">
|
||||
{{ baTable.form.operateIds && baTable.form.operateIds.length > 1 ? t('Save and edit next item') : t('Save') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, inject, useTemplateRef } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import type baTableClass from '/@/utils/baTable'
|
||||
import FormItem from '/@/components/formItem/index.vue'
|
||||
import type { ElTree, FormItemRule } from 'element-plus'
|
||||
import { buildValidatorData } from '/@/utils/validate'
|
||||
import type Node from 'element-plus/es/components/tree/src/model/node'
|
||||
import { useConfig } from '/@/stores/config'
|
||||
|
||||
const config = useConfig()
|
||||
const formRef = useTemplateRef('formRef')
|
||||
const treeRef = useTemplateRef('treeRef')
|
||||
const baTable = inject('baTable') as baTableClass
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const rules: Partial<Record<string, FormItemRule[]>> = reactive({
|
||||
name: [buildValidatorData({ name: 'required', title: t('auth.group.Group name') })],
|
||||
auth: [
|
||||
{
|
||||
required: true,
|
||||
validator: (rule: any, val: string, callback: Function) => {
|
||||
let ids = getCheckeds()
|
||||
if (ids.length <= 0) {
|
||||
return callback(new Error(t('Please select field', { field: t('auth.group.jurisdiction') })))
|
||||
}
|
||||
return callback()
|
||||
},
|
||||
},
|
||||
],
|
||||
pid: [
|
||||
{
|
||||
validator: (rule: any, val: string, callback: Function) => {
|
||||
if (!val) {
|
||||
return callback()
|
||||
}
|
||||
if (parseInt(val) == parseInt(baTable.form.items!.id)) {
|
||||
return callback(new Error(t('auth.group.The parent group cannot be the group itself')))
|
||||
}
|
||||
return callback()
|
||||
},
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const getCheckeds = () => {
|
||||
return treeRef.value!.getCheckedKeys().concat(treeRef.value!.getHalfCheckedKeys())
|
||||
}
|
||||
|
||||
const treeNodeClass = (data: anyObj, node: Node) => {
|
||||
if (node.isLeaf) return ''
|
||||
let addClass = true
|
||||
for (const key in node.childNodes) {
|
||||
if (!node.childNodes[key].isLeaf) {
|
||||
addClass = false
|
||||
}
|
||||
}
|
||||
return addClass ? 'penultimate-node' : ''
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
getCheckeds,
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
:deep(.penultimate-node) {
|
||||
.el-tree-node__children {
|
||||
padding-left: 60px;
|
||||
white-space: pre-wrap;
|
||||
line-height: 12px;
|
||||
.el-tree-node {
|
||||
display: inline-block;
|
||||
}
|
||||
.el-tree-node__content {
|
||||
padding-left: 5px !important;
|
||||
padding-right: 5px;
|
||||
.el-tree-node__expand-icon {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
196
web/src/views/backend/auth/rule/index.vue
Normal file
196
web/src/views/backend/auth/rule/index.vue
Normal file
@@ -0,0 +1,196 @@
|
||||
<template>
|
||||
<div class="default-main ba-table-box">
|
||||
<el-alert class="ba-table-alert" v-if="baTable.table.remark" :title="baTable.table.remark" type="info" show-icon />
|
||||
|
||||
<!-- 表格顶部菜单 -->
|
||||
<TableHeader
|
||||
:buttons="['refresh', 'add', 'edit', 'delete', 'unfold', 'quickSearch', 'columnDisplay']"
|
||||
:quick-search-placeholder="t('Quick search placeholder', { fields: t('auth.rule.title') })"
|
||||
/>
|
||||
|
||||
<!-- 设置合适的 max-height 实现隐藏布局主体部分本身的滚动条,这样就可以监听表格的 @scroll 了 -->
|
||||
<!-- max-height = 100vh - (当前布局顶栏高度 + 表头栏高度 + 表格上边距 + 预留的底部下边距) -->
|
||||
<Table
|
||||
ref="tableRef"
|
||||
:max-height="`calc(-${adminLayoutHeaderBarHeight[config.layout.layoutMode as keyof typeof adminLayoutHeaderBarHeight] + 75 + 16}px + 100vh)`"
|
||||
:pagination="false"
|
||||
@expand-change="onExpandChange"
|
||||
@scroll="onScroll"
|
||||
/>
|
||||
|
||||
<!-- 表单 -->
|
||||
<PopupForm />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { cloneDeep, debounce } from 'lodash-es'
|
||||
import { nextTick, onMounted, provide, useTemplateRef } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import PopupForm from './popupForm.vue'
|
||||
import { baTableApi } from '/@/api/common'
|
||||
import { defaultOptButtons } from '/@/components/table'
|
||||
import TableHeader from '/@/components/table/header/index.vue'
|
||||
import Table from '/@/components/table/index.vue'
|
||||
import { useConfig } from '/@/stores/config'
|
||||
import baTableClass from '/@/utils/baTable'
|
||||
import { adminLayoutHeaderBarHeight } from '/@/utils/layout'
|
||||
|
||||
defineOptions({
|
||||
name: 'auth/rule',
|
||||
})
|
||||
|
||||
const { t } = useI18n()
|
||||
const config = useConfig()
|
||||
const tableRef = useTemplateRef('tableRef')
|
||||
|
||||
const baTable = new baTableClass(
|
||||
new baTableApi('/admin/auth.Rule/'),
|
||||
{
|
||||
expandAll: false,
|
||||
dblClickNotEditColumn: [undefined, 'keepalive', 'status'],
|
||||
column: [
|
||||
{ type: 'selection', align: 'center' },
|
||||
{ label: t('auth.rule.title'), prop: 'title', align: 'left', width: '200' },
|
||||
{ label: t('auth.rule.Icon'), prop: 'icon', align: 'center', width: '60', render: 'icon', default: 'fa fa-circle-o' },
|
||||
{ label: t('auth.rule.name'), prop: 'name', align: 'center', showOverflowTooltip: true },
|
||||
{
|
||||
label: t('auth.rule.type'),
|
||||
prop: 'type',
|
||||
align: 'center',
|
||||
render: 'tag',
|
||||
custom: { menu: 'danger', menu_dir: 'success', button: 'info' },
|
||||
replaceValue: { menu: t('auth.rule.type menu'), menu_dir: t('auth.rule.type menu_dir'), button: t('auth.rule.type button') },
|
||||
},
|
||||
{ label: t('auth.rule.cache'), prop: 'keepalive', align: 'center', width: '80', render: 'switch' },
|
||||
{ label: t('State'), prop: 'status', align: 'center', width: '80', render: 'switch' },
|
||||
{ label: t('Update time'), prop: 'update_time', align: 'center', width: '160', render: 'datetime' },
|
||||
{
|
||||
label: t('Operate'),
|
||||
align: 'center',
|
||||
width: '130',
|
||||
render: 'buttons',
|
||||
buttons: defaultOptButtons(),
|
||||
},
|
||||
],
|
||||
dragSortLimitField: 'pid',
|
||||
},
|
||||
{
|
||||
defaultItems: {
|
||||
type: 'menu',
|
||||
menu_type: 'tab',
|
||||
extend: 'none',
|
||||
keepalive: 0,
|
||||
status: 1,
|
||||
icon: 'fa fa-circle-o',
|
||||
buttons: ['index', 'add', 'edit', 'del'],
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* 内存缓存表格的一些状态数据,供数据刷新后恢复
|
||||
*/
|
||||
const sessionStateDefault = {
|
||||
expanded: [] as any[],
|
||||
scrollTop: 0,
|
||||
scrollLeft: 0,
|
||||
expandAll: false,
|
||||
}
|
||||
let sessionState = sessionStateDefault
|
||||
|
||||
/**
|
||||
* 记录表格行展开状态
|
||||
*/
|
||||
const onExpandChange = (row: any, expanded: boolean) => {
|
||||
if (expanded) {
|
||||
sessionState.expanded.push(row)
|
||||
} else {
|
||||
sessionState.expanded = sessionState.expanded.filter((item: any) => item.id !== row.id)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录表格滚动条位置
|
||||
*/
|
||||
const onScroll = debounce(({ scrollLeft, scrollTop }: { scrollLeft: number; scrollTop: number }) => {
|
||||
sessionState.scrollTop = scrollTop
|
||||
sessionState.scrollLeft = scrollLeft
|
||||
}, 500)
|
||||
|
||||
/**
|
||||
* 记录表格行展开折叠状态
|
||||
*/
|
||||
const onUnfoldAll = (state: boolean) => {
|
||||
sessionState.expandAll = state
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复已记录的表格状态
|
||||
*/
|
||||
const restoreState = () => {
|
||||
nextTick(() => {
|
||||
const sessionStateTemp = sessionState
|
||||
|
||||
// 重置 sessionState 为默认值,恢复缓存的记录时将自动重设
|
||||
sessionState = cloneDeep(sessionStateDefault)
|
||||
|
||||
for (const key in sessionStateTemp.expanded) {
|
||||
tableRef.value?.getRef()?.toggleRowExpansion(sessionStateTemp.expanded[key], true)
|
||||
}
|
||||
nextTick(() => {
|
||||
if (sessionStateTemp.scrollTop || sessionStateTemp.scrollLeft) {
|
||||
tableRef.value?.getRef()?.scrollTo({ top: sessionStateTemp.scrollTop || 0, left: sessionStateTemp.scrollLeft || 0 })
|
||||
}
|
||||
|
||||
/**
|
||||
* expandAll 为 “是否默认展开所有行”
|
||||
* 此处表格数据已渲染,仅做顶部按钮状态标记用,不会实际上的执行展开折叠操作
|
||||
* 展开全部行之后,再只对某一行进行折叠时,expandAll 不会改变,所以此处并不根据 expandAll 值执行折叠展开所有行的操作
|
||||
*/
|
||||
baTable.table.expandAll = sessionStateTemp.expandAll
|
||||
onUnfoldAll(sessionStateTemp.expandAll)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 获取数据前钩子
|
||||
baTable.before.getData = () => {
|
||||
baTable.table.expandAll = baTable.table.filter?.quickSearch ? true : false
|
||||
}
|
||||
|
||||
// 获取到编辑行数据后的钩子
|
||||
baTable.after.getEditData = () => {
|
||||
if (baTable.form.items && !baTable.form.items.icon) {
|
||||
baTable.form.items.icon = 'fa fa-circle-o'
|
||||
}
|
||||
}
|
||||
|
||||
// 表格顶部按钮事件触发后的钩子
|
||||
baTable.after.onTableHeaderAction = ({ event, data }) => {
|
||||
if (event == 'unfold') {
|
||||
onUnfoldAll(data.unfold)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取到表格数据后的钩子
|
||||
baTable.after.getData = () => {
|
||||
restoreState()
|
||||
}
|
||||
|
||||
provide('baTable', baTable)
|
||||
|
||||
onMounted(() => {
|
||||
baTable.table.ref = tableRef.value
|
||||
baTable.mount()
|
||||
baTable.getData()?.then(() => {
|
||||
baTable.dragSort()
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.default-main {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
</style>
|
||||
244
web/src/views/backend/auth/rule/popupForm.vue
Normal file
244
web/src/views/backend/auth/rule/popupForm.vue
Normal file
@@ -0,0 +1,244 @@
|
||||
<template>
|
||||
<!-- 对话框表单 -->
|
||||
<el-dialog
|
||||
class="ba-operate-dialog"
|
||||
:close-on-click-modal="false"
|
||||
:destroy-on-close="true"
|
||||
:model-value="['Add', 'Edit'].includes(baTable.form.operate!)"
|
||||
@close="baTable.toggleForm"
|
||||
>
|
||||
<template #header>
|
||||
<div class="title" v-drag="['.ba-operate-dialog', '.el-dialog__header']" v-zoom="'.ba-operate-dialog'">
|
||||
{{ baTable.form.operate ? t(baTable.form.operate) : '' }}
|
||||
</div>
|
||||
</template>
|
||||
<el-scrollbar v-loading="baTable.form.loading" class="ba-table-form-scrollbar">
|
||||
<div
|
||||
class="ba-operate-form"
|
||||
:class="'ba-' + baTable.form.operate + '-form'"
|
||||
:style="config.layout.shrink ? '' : 'width: calc(100% - ' + baTable.form.labelWidth! / 2 + 'px)'"
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
@keyup.enter="baTable.onSubmit(formRef)"
|
||||
:model="baTable.form.items"
|
||||
:label-position="config.layout.shrink ? 'top' : 'right'"
|
||||
:label-width="baTable.form.labelWidth + 'px'"
|
||||
:rules="rules"
|
||||
v-if="!baTable.form.loading"
|
||||
>
|
||||
<FormItem
|
||||
type="remoteSelect"
|
||||
prop="pid"
|
||||
:label="t('auth.rule.Superior menu rule')"
|
||||
v-model="baTable.form.items!.pid"
|
||||
:placeholder="t('Click select')"
|
||||
:input-attr="{
|
||||
params: { isTree: true },
|
||||
field: 'title',
|
||||
remoteUrl: baTable.api.actionUrl.get('index'),
|
||||
emptyValues: ['', null, undefined, 0],
|
||||
valueOnClear: 0,
|
||||
}"
|
||||
/>
|
||||
<FormItem
|
||||
:label="t('auth.rule.Rule type')"
|
||||
v-model="baTable.form.items!.type"
|
||||
type="radio"
|
||||
:input-attr="{
|
||||
border: true,
|
||||
content: { menu_dir: t('auth.rule.type menu_dir'), menu: t('auth.rule.type menu'), button: t('auth.rule.type button') },
|
||||
}"
|
||||
/>
|
||||
<el-form-item prop="title" :label="t('auth.rule.Rule title')">
|
||||
<el-input
|
||||
v-model="baTable.form.items!.title"
|
||||
type="string"
|
||||
:placeholder="t('Please input field', { field: t('auth.rule.Rule title') })"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="name" :label="t('auth.rule.Rule name')">
|
||||
<el-input
|
||||
v-model="baTable.form.items!.name"
|
||||
type="string"
|
||||
:placeholder="t('auth.rule.English name, which does not need to start with `/admin`, such as auth/menu')"
|
||||
></el-input>
|
||||
<div class="block-help">
|
||||
{{ t('auth.rule.It will be registered as the web side routing name and used as the server side API authentication') }}
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item prop="path" v-if="baTable.form.items!.type != 'button'" :label="t('auth.rule.Routing path')">
|
||||
<el-input
|
||||
v-model="baTable.form.items!.path"
|
||||
type="string"
|
||||
:placeholder="t('auth.rule.The web side routing path (path) does not need to start with `/admin`, such as auth/menu')"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<FormItem
|
||||
v-if="baTable.form.operate && baTable.form.items!.type != 'button'"
|
||||
type="icon"
|
||||
:label="t('auth.rule.Rule Icon')"
|
||||
v-model="baTable.form.items!.icon"
|
||||
:input-attr="{
|
||||
showIconName: true,
|
||||
}"
|
||||
/>
|
||||
<FormItem
|
||||
v-if="baTable.form.items!.type == 'menu'"
|
||||
:label="t('auth.rule.Menu type')"
|
||||
v-model="baTable.form.items!.menu_type"
|
||||
type="radio"
|
||||
:input-attr="{
|
||||
border: true,
|
||||
content: { tab: t('auth.rule.Menu type tab'), link: t('auth.rule.Menu type link (offsite)'), iframe: 'Iframe' },
|
||||
}"
|
||||
/>
|
||||
<el-form-item
|
||||
prop="url"
|
||||
v-if="baTable.form.items!.menu_type != 'tab' && baTable.form.items!.type == 'menu'"
|
||||
:label="t('auth.rule.Link address')"
|
||||
>
|
||||
<el-input
|
||||
v-model="baTable.form.items!.url"
|
||||
type="string"
|
||||
:placeholder="t('auth.rule.Please enter the URL address of the link or iframe')"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
prop="component"
|
||||
v-if="baTable.form.items!.type == 'menu' && baTable.form.items!.menu_type == 'tab'"
|
||||
:label="t('auth.rule.Component path')"
|
||||
>
|
||||
<el-input
|
||||
v-model="baTable.form.items!.component"
|
||||
type="string"
|
||||
:placeholder="t('auth.rule.Web side component path, please start with /src, such as: /src/views/backend/dashboard')"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="baTable.form.items!.type == 'menu' && baTable.form.items!.menu_type == 'tab'"
|
||||
:label="t('auth.rule.Extended properties')"
|
||||
>
|
||||
<el-select
|
||||
class="w100"
|
||||
v-model="baTable.form.items!.extend"
|
||||
:placeholder="t('Please select field', { field: t('auth.rule.Extended properties') })"
|
||||
>
|
||||
<el-option :label="t('auth.rule.none')" value="none"></el-option>
|
||||
<el-option :label="t('auth.rule.Add as route only')" value="add_rules_only"></el-option>
|
||||
<el-option :label="t('auth.rule.Add as menu only')" value="add_menu_only"></el-option>
|
||||
</el-select>
|
||||
<div class="block-help">{{ t('auth.rule.extend Title') }}</div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('auth.rule.Rule comments')">
|
||||
<el-input
|
||||
@keyup.enter.stop=""
|
||||
@keyup.ctrl.enter="baTable.onSubmit(formRef)"
|
||||
v-model="baTable.form.items!.remark"
|
||||
type="textarea"
|
||||
:autosize="{ minRows: 2, maxRows: 5 }"
|
||||
:placeholder="
|
||||
t(
|
||||
'auth.rule.Use in controller `get_ route_ Remark()` function, which can obtain the value of this field for your own use, such as the banner file of the console'
|
||||
)
|
||||
"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('auth.rule.Rule weight')">
|
||||
<el-input
|
||||
v-model="baTable.form.items!.weigh"
|
||||
type="number"
|
||||
:placeholder="t('auth.rule.Please enter the weight of menu rule (sort by)')"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<FormItem
|
||||
v-if="baTable.form.operate == 'Add' && baTable.form.items!.type == 'menu'"
|
||||
:label="t('auth.rule.Create Page Button')"
|
||||
v-model="baTable.form.items!.buttons"
|
||||
type="selects"
|
||||
:input-attr="{
|
||||
content: {
|
||||
index: t('auth.rule.Create Page Button index'),
|
||||
add: t('auth.rule.Create Page Button add'),
|
||||
edit: t('auth.rule.Create Page Button edit'),
|
||||
del: t('auth.rule.Create Page Button del'),
|
||||
sortable: t('auth.rule.Create Page Button sortable'),
|
||||
},
|
||||
}"
|
||||
:placeholder="t('auth.rule.Please select the button for automatically creating the desired page')"
|
||||
:block-help="t('auth.rule.Create Page Button tips')"
|
||||
/>
|
||||
<FormItem
|
||||
:label="t('auth.rule.cache')"
|
||||
v-model="baTable.form.items!.keepalive"
|
||||
type="radio"
|
||||
:input-attr="{
|
||||
border: true,
|
||||
content: { 0: t('Disable'), 1: t('Enable') },
|
||||
}"
|
||||
/>
|
||||
<FormItem
|
||||
:label="t('State')"
|
||||
v-model="baTable.form.items!.status"
|
||||
type="radio"
|
||||
:input-attr="{
|
||||
border: true,
|
||||
content: { 0: t('Disable'), 1: t('Enable') },
|
||||
}"
|
||||
/>
|
||||
</el-form>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
<template #footer>
|
||||
<div :style="'width: calc(100% - ' + baTable.form.labelWidth! / 1.8 + 'px)'">
|
||||
<el-button @click="baTable.toggleForm('')">{{ t('Cancel') }}</el-button>
|
||||
<el-button v-blur :loading="baTable.form.submitLoading" @click="baTable.onSubmit(formRef)" type="primary">
|
||||
{{ baTable.form.operateIds && baTable.form.operateIds.length > 1 ? t('Save and edit next item') : t('Save') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, inject, useTemplateRef } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import type baTableClass from '/@/utils/baTable'
|
||||
import FormItem from '/@/components/formItem/index.vue'
|
||||
import { buildValidatorData } from '/@/utils/validate'
|
||||
import type { FormItemRule } from 'element-plus'
|
||||
import { useConfig } from '/@/stores/config'
|
||||
|
||||
const config = useConfig()
|
||||
const formRef = useTemplateRef('formRef')
|
||||
const baTable = inject('baTable') as baTableClass
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const rules: Partial<Record<string, FormItemRule[]>> = reactive({
|
||||
title: [buildValidatorData({ name: 'required', title: t('auth.rule.Rule title') })],
|
||||
name: [buildValidatorData({ name: 'required', title: t('auth.rule.Rule name') })],
|
||||
path: [buildValidatorData({ name: 'required', title: t('auth.rule.Routing path') })],
|
||||
url: [
|
||||
buildValidatorData({ name: 'required', title: t('auth.rule.Link address') }),
|
||||
buildValidatorData({ name: 'url', message: t('auth.rule.Please enter the correct URL') }),
|
||||
],
|
||||
component: [buildValidatorData({ name: 'required', message: t('auth.rule.Component path') })],
|
||||
pid: [
|
||||
{
|
||||
validator: (rule: any, val: string, callback: Function) => {
|
||||
if (!val) {
|
||||
return callback()
|
||||
}
|
||||
if (parseInt(val) == parseInt(baTable.form.items!.id)) {
|
||||
return callback(new Error(t('auth.rule.The superior menu rule cannot be the rule itself')))
|
||||
}
|
||||
return callback()
|
||||
},
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
Reference in New Issue
Block a user