mirror of
https://gitee.com/hhyykk/ipms-sjy.git
synced 2025-09-11 15:41:54 +08:00
feat: add vue3(element-plus)
This commit is contained in:
55
yudao-ui-admin-vue3/src/views/system/dept/dept.data.ts
Normal file
55
yudao-ui-admin-vue3/src/views/system/dept/dept.data.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { required } from '@/utils/formRules'
|
||||
import { reactive } from 'vue'
|
||||
|
||||
export const modelSchema = reactive<FormSchema[]>([
|
||||
{
|
||||
label: '上级部门',
|
||||
field: 'parentId',
|
||||
component: 'Input'
|
||||
},
|
||||
{
|
||||
label: '部门名称',
|
||||
field: 'name',
|
||||
component: 'Input',
|
||||
formItemProps: {
|
||||
rules: [required]
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '负责人',
|
||||
field: 'email',
|
||||
component: 'Input'
|
||||
},
|
||||
{
|
||||
label: '联系电话',
|
||||
field: 'phone',
|
||||
component: 'Input'
|
||||
},
|
||||
{
|
||||
label: '邮箱',
|
||||
field: 'email',
|
||||
component: 'Input'
|
||||
},
|
||||
{
|
||||
label: '显示排序',
|
||||
field: 'sort',
|
||||
component: 'Input'
|
||||
},
|
||||
{
|
||||
label: '状态',
|
||||
field: 'status',
|
||||
component: 'RadioButton',
|
||||
componentProps: {
|
||||
options: [
|
||||
{
|
||||
label: '开启',
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
label: '关闭',
|
||||
value: 1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
])
|
189
yudao-ui-admin-vue3/src/views/system/dept/index.vue
Normal file
189
yudao-ui-admin-vue3/src/views/system/dept/index.vue
Normal file
@@ -0,0 +1,189 @@
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { ElCard, ElTree, ElTreeSelect, ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { handleTree } from '@/utils/tree'
|
||||
import { onMounted, ref, unref } from 'vue'
|
||||
import * as DeptApi from '@/api/system/dept'
|
||||
import { Form, FormExpose } from '@/components/Form'
|
||||
import { modelSchema } from './dept.data'
|
||||
import { DeptVO } from '@/api/system/dept/types'
|
||||
interface Tree {
|
||||
id: number
|
||||
name: string
|
||||
children?: Tree[]
|
||||
}
|
||||
|
||||
const defaultProps = {
|
||||
children: 'children',
|
||||
label: 'name',
|
||||
value: 'id'
|
||||
}
|
||||
const { t } = useI18n() // 国际化
|
||||
const loading = ref(false) // 遮罩层
|
||||
const dialogVisible = ref(false) // 是否显示弹出层
|
||||
const showForm = ref(false) // 显示form表单
|
||||
const formTitle = ref('部门信息') // 显示form标题
|
||||
const deptParentId = ref(0) // 上级ID
|
||||
// 创建form表单
|
||||
const formRef = ref<FormExpose>()
|
||||
|
||||
// ========== 创建部门树结构 ==========
|
||||
const deptOptions = ref([]) // 树形结构
|
||||
const treeRef = ref<InstanceType<typeof ElTree>>()
|
||||
const getTree = async () => {
|
||||
const res = await DeptApi.listSimpleDeptApi()
|
||||
deptOptions.value = handleTree(res)
|
||||
}
|
||||
const filterNode = (value: string, data: Tree) => {
|
||||
if (!value) return true
|
||||
return data.name.includes(value)
|
||||
}
|
||||
// 新增
|
||||
const handleAdd = () => {
|
||||
// 重置表单
|
||||
formTitle.value = '新增部门'
|
||||
unref(formRef)?.getElFormRef()?.resetFields()
|
||||
showForm.value = true
|
||||
}
|
||||
// 编辑
|
||||
const handleUpdate = async (data: { id: number }) => {
|
||||
showForm.value = true
|
||||
const res = await DeptApi.getDeptApi(data.id)
|
||||
formTitle.value = '修改- ' + res?.name
|
||||
deptParentId.value = res.parentId
|
||||
unref(formRef)?.setValues(res)
|
||||
}
|
||||
// 删除
|
||||
const handleDelete = async (data: { id: number }) => {
|
||||
ElMessageBox.confirm(t('common.delDataMessage'), t('common.confirmTitle'), {
|
||||
confirmButtonText: t('common.ok'),
|
||||
cancelButtonText: t('common.cancel'),
|
||||
type: 'warning'
|
||||
})
|
||||
.then(async () => {
|
||||
await DeptApi.deleteDeptApi(data.id)
|
||||
ElMessage.success(t('common.delSuccess'))
|
||||
})
|
||||
.catch(() => {})
|
||||
await getTree()
|
||||
}
|
||||
// 提交按钮
|
||||
const submitForm = async () => {
|
||||
loading.value = true
|
||||
// 提交请求
|
||||
try {
|
||||
const data = unref(formRef)?.formModel as DeptVO
|
||||
deptParentId.value = data.parentId
|
||||
// TODO: 表单提交待完善
|
||||
if (formTitle.value.startsWith('新增')) {
|
||||
await DeptApi.createDeptApi(data)
|
||||
} else if (formTitle.value.startsWith('修改')) {
|
||||
await DeptApi.updateDeptApi(data)
|
||||
}
|
||||
// 操作成功,重新加载列表
|
||||
dialogVisible.value = false
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
onMounted(async () => {
|
||||
await getTree()
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<div class="flex">
|
||||
<el-card class="w-1/3 dept" :gutter="12" shadow="always">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>部门列表</span>
|
||||
<el-button type="primary" v-hasPermi="['system:dept:create']" @click="handleAdd">
|
||||
新增根节点
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<div class="custom-tree-container">
|
||||
<!-- <p>部门列表</p> -->
|
||||
<!-- 操作工具栏 -->
|
||||
<el-tree
|
||||
ref="treeRef"
|
||||
node-key="id"
|
||||
:data="deptOptions"
|
||||
:props="defaultProps"
|
||||
:highlight-current="true"
|
||||
default-expand-all
|
||||
:filter-method="filterNode"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<span class="custom-tree-node">
|
||||
<span>{{ node.label }}</span>
|
||||
<span>
|
||||
<el-button link v-hasPermi="['system:dept:create']" @click="handleAdd()">
|
||||
<Icon icon="ep:plus" class="mr-5px" />
|
||||
</el-button>
|
||||
<el-button link v-hasPermi="['system:dept:update']" @click="handleUpdate(data)">
|
||||
<Icon icon="ep:edit" class="mr-5px" />
|
||||
</el-button>
|
||||
<el-button link v-hasPermi="['system:dept:delete']" @click="handleDelete(data)">
|
||||
<Icon icon="ep:delete" class="mr-5px" />
|
||||
</el-button>
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card class="w-2/3 dept" style="margin-left: 10px" :gutter="12" shadow="hover">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>{{ formTitle }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<div v-if="!showForm">
|
||||
<span><p>请从左侧选择部门</p></span>
|
||||
</div>
|
||||
<div v-if="showForm">
|
||||
<!-- 操作工具栏 -->
|
||||
<Form :schema="modelSchema" ref="formRef">
|
||||
<template #parentId>
|
||||
<el-tree-select
|
||||
node-key="id"
|
||||
v-model="deptParentId"
|
||||
:props="defaultProps"
|
||||
:data="deptOptions"
|
||||
check-strictly
|
||||
/>
|
||||
</template>
|
||||
</Form>
|
||||
<!-- 操作按钮 -->
|
||||
<el-button
|
||||
type="primary"
|
||||
v-hasPermi="['system:dept:update']"
|
||||
:loading="loading"
|
||||
@click="submitForm"
|
||||
>
|
||||
{{ t('action.save') }}
|
||||
</el-button>
|
||||
<el-button type="danger" @click="showForm = false">取消</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
<style scoped>
|
||||
.dept {
|
||||
height: 600px;
|
||||
max-height: 1800px;
|
||||
}
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.custom-tree-node {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 14px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
</style>
|
128
yudao-ui-admin-vue3/src/views/system/dict/dict.data.ts
Normal file
128
yudao-ui-admin-vue3/src/views/system/dict/dict.data.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
import { reactive } from 'vue'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { required } from '@/utils/formRules'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { CrudSchema, useCrudSchemas } from '@/hooks/web/useCrudSchemas'
|
||||
// 国际化
|
||||
const { t } = useI18n()
|
||||
// 表单校验
|
||||
export const dictDataRules = reactive({
|
||||
dictType: [required],
|
||||
label: [required],
|
||||
value: [required]
|
||||
})
|
||||
// crudSchemas
|
||||
export const crudSchemas = reactive<CrudSchema[]>([
|
||||
{
|
||||
label: t('common.index'),
|
||||
field: 'id',
|
||||
type: 'index',
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '字典类型',
|
||||
field: 'dictType',
|
||||
table: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '数据标签',
|
||||
field: 'label',
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '数据键值',
|
||||
field: 'value'
|
||||
},
|
||||
{
|
||||
label: '颜色类型',
|
||||
field: 'colorType',
|
||||
form: {
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
options: [
|
||||
{
|
||||
label: 'default',
|
||||
value: ''
|
||||
},
|
||||
{
|
||||
label: 'success',
|
||||
value: 'success'
|
||||
},
|
||||
{
|
||||
label: 'info',
|
||||
value: 'info'
|
||||
},
|
||||
{
|
||||
label: 'warning',
|
||||
value: 'warning'
|
||||
},
|
||||
{
|
||||
label: 'danger',
|
||||
value: 'danger'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
table: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'CSS Class',
|
||||
field: 'cssClass',
|
||||
table: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '显示排序',
|
||||
field: 'sort',
|
||||
table: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('common.status'),
|
||||
field: 'status',
|
||||
dictType: DICT_TYPE.COMMON_STATUS
|
||||
},
|
||||
{
|
||||
label: t('form.remark'),
|
||||
field: 'remark',
|
||||
form: {
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
type: 'textarea',
|
||||
rows: 4
|
||||
},
|
||||
colProps: {
|
||||
span: 24
|
||||
}
|
||||
},
|
||||
table: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('table.action'),
|
||||
field: 'action',
|
||||
width: '180px',
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
export const { allSchemas } = useCrudSchemas(crudSchemas)
|
74
yudao-ui-admin-vue3/src/views/system/dict/dict.type.ts
Normal file
74
yudao-ui-admin-vue3/src/views/system/dict/dict.type.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { reactive } from 'vue'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { required } from '@/utils/formRules'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { CrudSchema, useCrudSchemas } from '@/hooks/web/useCrudSchemas'
|
||||
// 国际化
|
||||
const { t } = useI18n()
|
||||
// 表单校验
|
||||
export const dictTypeRules = reactive({
|
||||
name: [required],
|
||||
type: [required]
|
||||
})
|
||||
// 新增 + 修改
|
||||
const crudSchemas = reactive<CrudSchema[]>([
|
||||
{
|
||||
label: t('common.index'),
|
||||
field: 'id',
|
||||
type: 'index',
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '字典名称',
|
||||
field: 'name',
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '字典类型',
|
||||
field: 'type',
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('common.status'),
|
||||
field: 'status',
|
||||
dictType: DICT_TYPE.COMMON_STATUS
|
||||
},
|
||||
{
|
||||
label: t('form.remark'),
|
||||
field: 'remark',
|
||||
table: {
|
||||
show: false
|
||||
},
|
||||
form: {
|
||||
componentProps: {
|
||||
type: 'textarea',
|
||||
rows: 4
|
||||
},
|
||||
colProps: {
|
||||
span: 24
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
width: '180px',
|
||||
label: t('table.action'),
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
export const { allSchemas } = useCrudSchemas(crudSchemas)
|
298
yudao-ui-admin-vue3/src/views/system/dict/index.vue
Normal file
298
yudao-ui-admin-vue3/src/views/system/dict/index.vue
Normal file
@@ -0,0 +1,298 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, unref, onMounted } from 'vue'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { FormExpose } from '@/components/Form'
|
||||
import * as DictTypeSchemas from './dict.type'
|
||||
import * as DictDataSchemas from './dict.data'
|
||||
import { useTable } from '@/hooks/web/useTable'
|
||||
import { ElCard, ElMessage } from 'element-plus'
|
||||
import * as DictTypeApi from '@/api/system/dict/dict.type'
|
||||
import * as DictDataApi from '@/api/system/dict/dict.data'
|
||||
import { DictDataVO, DictTypeVO } from '@/api/system/dict/types'
|
||||
const { t } = useI18n() // 国际化
|
||||
// ========== 字典分类列表相关 ==========
|
||||
const {
|
||||
register: typeRegister,
|
||||
tableObject: typeTableObject,
|
||||
methods: typeMethods
|
||||
} = useTable<PageResult<DictTypeVO>, DictTypeVO>({
|
||||
getListApi: DictTypeApi.getDictTypePageApi,
|
||||
delListApi: DictTypeApi.deleteDictTypeApi
|
||||
})
|
||||
const {
|
||||
getList: getTypeList,
|
||||
setSearchParams: setTypeSearchParams,
|
||||
delList: delTypeList
|
||||
} = typeMethods
|
||||
|
||||
// 字典分类修改操作
|
||||
const handleTypeCreate = () => {
|
||||
setDialogTile('typeCreate')
|
||||
// 重置表单
|
||||
unref(typeFormRef)?.getElFormRef()?.resetFields()
|
||||
}
|
||||
const handleTypeUpdate = async (row: DictTypeVO) => {
|
||||
setDialogTile('typeUpdate')
|
||||
// 设置数据
|
||||
const res = await DictTypeApi.getDictTypeApi(row.id)
|
||||
unref(typeFormRef)?.setValues(res)
|
||||
}
|
||||
// 字典分类删除操作
|
||||
const handleTypeDelete = async (row: DictTypeVO) => {
|
||||
await delTypeList(row.id, false)
|
||||
}
|
||||
|
||||
// ========== 字典数据列表相关 ==========
|
||||
const tableTypeSelect = ref(false)
|
||||
const {
|
||||
register: dataRegister,
|
||||
tableObject: dataTableObject,
|
||||
methods: dataMethods
|
||||
} = useTable<PageResult<DictDataVO>, DictDataVO>({
|
||||
getListApi: DictDataApi.getDictDataPageApi,
|
||||
delListApi: DictDataApi.deleteDictDataApi
|
||||
})
|
||||
const {
|
||||
getList: getDataList,
|
||||
setSearchParams: setDataSearchParams,
|
||||
delList: delDataList
|
||||
} = dataMethods
|
||||
|
||||
// 字典数据修改操作
|
||||
const handleDataCreate = () => {
|
||||
setDialogTile('dataCreate')
|
||||
// 重置表单
|
||||
unref(dataFormRef)?.getElFormRef()?.resetFields()
|
||||
}
|
||||
const handleDataUpdate = async (row: DictDataVO) => {
|
||||
setDialogTile('dataUpdate')
|
||||
// 设置数据
|
||||
const res = await DictDataApi.getDictDataApi(row.id)
|
||||
unref(dataFormRef)?.setValues(res)
|
||||
}
|
||||
// 字典数据删除操作
|
||||
const handleDataDelete = async (row: DictTypeVO) => {
|
||||
await delDataList(row.id, false)
|
||||
}
|
||||
// 字典分类点击行事件
|
||||
const parentType = ref('')
|
||||
const onClickType = async (data: { [key: string]: any }) => {
|
||||
tableTypeSelect.value = true
|
||||
dataTableObject.paramsObj.params = {
|
||||
dictType: data.type
|
||||
}
|
||||
getDataList()
|
||||
parentType.value = data.type
|
||||
}
|
||||
// 弹出框
|
||||
const dialogVisible = ref(false) // 是否显示弹出层
|
||||
const dialogTitle = ref('edit') // 弹出层标题
|
||||
const actionLoading = ref(false) // 遮罩层
|
||||
const typeFormRef = ref<FormExpose>() // 分类表单 Ref
|
||||
const dataFormRef = ref<FormExpose>() // 数据表单 Ref
|
||||
const actionType = ref('') // 操作按钮的类型
|
||||
// 设置标题
|
||||
const setDialogTile = (type: string) => {
|
||||
dialogTitle.value = t('action.' + type)
|
||||
actionType.value = type
|
||||
dialogVisible.value = true
|
||||
}
|
||||
// 提交按钮
|
||||
const submitTypeForm = async () => {
|
||||
actionLoading.value = true
|
||||
// 提交请求
|
||||
try {
|
||||
const data = unref(typeFormRef)?.formModel as DictTypeVO
|
||||
if (actionType.value === 'typeCreate') {
|
||||
await DictTypeApi.createDictTypeApi(data)
|
||||
ElMessage.success(t('common.createSuccess'))
|
||||
} else if (actionType.value === 'typeUpdate') {
|
||||
await DictTypeApi.updateDictTypeApi(data)
|
||||
ElMessage.success(t('common.updateSuccess'))
|
||||
}
|
||||
// 操作成功,重新加载列表
|
||||
await getTypeList()
|
||||
dialogVisible.value = false
|
||||
} finally {
|
||||
actionLoading.value = false
|
||||
}
|
||||
}
|
||||
const submitDataForm = async () => {
|
||||
actionLoading.value = true
|
||||
// 提交请求
|
||||
try {
|
||||
const data = unref(dataFormRef)?.formModel as DictDataVO
|
||||
if (actionType.value === 'dataCreate') {
|
||||
data.dictType = parentType.value
|
||||
await DictDataApi.createDictDataApi(data)
|
||||
ElMessage.success(t('common.createSuccess'))
|
||||
} else if (actionType.value === 'dataUpdate') {
|
||||
await DictDataApi.updateDictDataApi(data)
|
||||
ElMessage.success(t('common.updateSuccess'))
|
||||
}
|
||||
await getDataList()
|
||||
// 操作成功,重新加载列表
|
||||
dialogVisible.value = false
|
||||
} finally {
|
||||
actionLoading.value = false
|
||||
}
|
||||
}
|
||||
// 初始化查询
|
||||
onMounted(async () => {
|
||||
await getTypeList()
|
||||
typeTableObject.tableList[0] && onClickType(typeTableObject.tableList[0])
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex">
|
||||
<el-card class="w-1/2 dict" :gutter="12" shadow="always">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>字典分类</span>
|
||||
</div>
|
||||
</template>
|
||||
<Search
|
||||
:schema="DictTypeSchemas.allSchemas.searchSchema"
|
||||
@search="setTypeSearchParams"
|
||||
@reset="setTypeSearchParams"
|
||||
/>
|
||||
<!-- 操作工具栏 -->
|
||||
<div class="mb-10px">
|
||||
<el-button type="primary" v-hasPermi="['system:dict:create']" @click="handleTypeCreate">
|
||||
<Icon icon="el:zoom-in" class="mr-5px" /> {{ t('action.add') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<!-- 列表 -->
|
||||
<Table
|
||||
@row-click="onClickType"
|
||||
:columns="DictTypeSchemas.allSchemas.tableColumns"
|
||||
:selection="false"
|
||||
:data="typeTableObject.tableList"
|
||||
:loading="typeTableObject.loading"
|
||||
:pagination="{
|
||||
total: typeTableObject.total
|
||||
}"
|
||||
:highlight-current-row="true"
|
||||
v-model:pageSize="typeTableObject.pageSize"
|
||||
v-model:currentPage="typeTableObject.currentPage"
|
||||
@register="typeRegister"
|
||||
>
|
||||
<template #status="{ row }">
|
||||
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:dict:update']"
|
||||
@click="handleTypeUpdate(row)"
|
||||
>
|
||||
<Icon icon="ep:edit" class="mr-5px" /> {{ t('action.edit') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:dict:delete']"
|
||||
@click="handleTypeDelete(row)"
|
||||
>
|
||||
<Icon icon="ep:delete" class="mr-5px" /> {{ t('action.del') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</Table>
|
||||
</el-card>
|
||||
<el-card class="w-1/2 dict" style="margin-left: 10px" :gutter="12" shadow="hover">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>字典数据</span>
|
||||
</div>
|
||||
</template>
|
||||
<!-- 列表 -->
|
||||
<div v-if="!tableTypeSelect">
|
||||
<span>请从左侧选择</span>
|
||||
</div>
|
||||
<div v-if="tableTypeSelect">
|
||||
<Search
|
||||
:schema="DictDataSchemas.allSchemas.searchSchema"
|
||||
@search="setDataSearchParams"
|
||||
@reset="setDataSearchParams"
|
||||
/>
|
||||
<!-- 操作工具栏 -->
|
||||
<div class="mb-10px">
|
||||
<el-button type="primary" v-hasPermi="['system:dict:create']" @click="handleDataCreate">
|
||||
<Icon icon="el:zoom-in" class="mr-5px" /> {{ t('action.add') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<Table
|
||||
:columns="DictDataSchemas.allSchemas.tableColumns"
|
||||
:selection="false"
|
||||
:data="dataTableObject.tableList"
|
||||
:loading="dataTableObject.loading"
|
||||
:pagination="{
|
||||
total: dataTableObject.total
|
||||
}"
|
||||
v-model:pageSize="dataTableObject.pageSize"
|
||||
v-model:currentPage="dataTableObject.currentPage"
|
||||
@register="dataRegister"
|
||||
>
|
||||
<template #status="{ row }">
|
||||
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:dict:update']"
|
||||
@click="handleDataUpdate(row)"
|
||||
>
|
||||
<Icon icon="ep:edit" class="mr-5px" /> {{ t('action.edit') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:dict:delete']"
|
||||
@click="handleDataDelete(row)"
|
||||
>
|
||||
<Icon icon="ep:delete" class="mr-5px" /> {{ t('action.del') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</Table>
|
||||
</div>
|
||||
</el-card>
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle">
|
||||
<Form
|
||||
v-if="['typeCreate', 'typeUpdate'].includes(actionType)"
|
||||
:schema="DictTypeSchemas.allSchemas.formSchema"
|
||||
:rules="DictTypeSchemas.dictTypeRules"
|
||||
ref="typeFormRef"
|
||||
/>
|
||||
<Form
|
||||
v-if="['dataCreate', 'dataUpdate'].includes(actionType)"
|
||||
:schema="DictDataSchemas.allSchemas.formSchema"
|
||||
:rules="DictDataSchemas.dictDataRules"
|
||||
ref="dataFormRef"
|
||||
/>
|
||||
<!-- 操作按钮 -->
|
||||
<template #footer>
|
||||
<el-button
|
||||
v-if="['typeCreate', 'typeUpdate'].includes(actionType)"
|
||||
type="primary"
|
||||
:loading="actionLoading"
|
||||
@click="submitTypeForm"
|
||||
>
|
||||
{{ t('action.save') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="['dataCreate', 'dataUpdate'].includes(actionType)"
|
||||
type="primary"
|
||||
:loading="actionLoading"
|
||||
@click="submitDataForm"
|
||||
>
|
||||
{{ t('action.save') }}
|
||||
</el-button>
|
||||
<el-button @click="dialogVisible = false">{{ t('dialog.close') }}</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</div>
|
||||
</template>
|
@@ -0,0 +1,94 @@
|
||||
import { reactive } from 'vue'
|
||||
import { required } from '@/utils/formRules'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { CrudSchema, useCrudSchemas } from '@/hooks/web/useCrudSchemas'
|
||||
// 国际化
|
||||
const { t } = useI18n()
|
||||
// 表单校验
|
||||
export const rules = reactive({
|
||||
applicationName: [required],
|
||||
code: [required],
|
||||
message: [required]
|
||||
})
|
||||
// 新增 + 修改
|
||||
const crudSchemas = reactive<CrudSchema[]>([
|
||||
{
|
||||
label: t('common.index'),
|
||||
field: 'id',
|
||||
type: 'index',
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '错误码类型',
|
||||
field: 'type',
|
||||
component: 'InputNumber',
|
||||
dictType: DICT_TYPE.SYSTEM_ERROR_CODE_TYPE,
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '应用名',
|
||||
field: 'applicationName',
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '错误码编码',
|
||||
field: 'code',
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '错误码错误提示',
|
||||
field: 'message'
|
||||
},
|
||||
{
|
||||
label: t('common.createTime'),
|
||||
field: 'createTime',
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('common.createTime'),
|
||||
field: 'daterange',
|
||||
table: {
|
||||
show: false
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
},
|
||||
search: {
|
||||
show: true,
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
type: 'daterange',
|
||||
valueFormat: 'YYYY-MM-DD'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
width: '240px',
|
||||
label: t('table.action'),
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
])
|
||||
export const { allSchemas } = useCrudSchemas(crudSchemas)
|
184
yudao-ui-admin-vue3/src/views/system/errorCode/index.vue
Normal file
184
yudao-ui-admin-vue3/src/views/system/errorCode/index.vue
Normal file
@@ -0,0 +1,184 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, unref } from 'vue'
|
||||
import dayjs from 'dayjs'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { useTable } from '@/hooks/web/useTable'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { FormExpose } from '@/components/Form'
|
||||
import type { ErrorCodeVO } from '@/api/system/errorCode/types'
|
||||
import { rules, allSchemas } from './errorCode.data'
|
||||
import * as ErrorCodeApi from '@/api/system/errorCode'
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
// ========== 列表相关 ==========
|
||||
const { register, tableObject, methods } = useTable<PageResult<ErrorCodeVO>, ErrorCodeVO>({
|
||||
getListApi: ErrorCodeApi.getErrorCodePageApi,
|
||||
delListApi: ErrorCodeApi.deleteErrorCodeApi
|
||||
})
|
||||
const { getList, setSearchParams, delList } = methods
|
||||
|
||||
// ========== CRUD 相关 ==========
|
||||
const loading = ref(false) // 遮罩层
|
||||
const actionType = ref('') // 操作按钮的类型
|
||||
const dialogVisible = ref(false) // 是否显示弹出层
|
||||
const dialogTitle = ref('edit') // 弹出层标题
|
||||
const formRef = ref<FormExpose>() // 表单 Ref
|
||||
|
||||
// 设置标题
|
||||
const setDialogTile = (type: string) => {
|
||||
dialogTitle.value = t('action.' + type)
|
||||
actionType.value = type
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
// 新增操作
|
||||
const handleCreate = () => {
|
||||
setDialogTile('create')
|
||||
// 重置表单
|
||||
unref(formRef)?.getElFormRef()?.resetFields()
|
||||
}
|
||||
|
||||
// 修改操作
|
||||
const handleUpdate = async (row: ErrorCodeVO) => {
|
||||
setDialogTile('update')
|
||||
// 设置数据
|
||||
const res = await ErrorCodeApi.getErrorCodeApi(row.id)
|
||||
unref(formRef)?.setValues(res)
|
||||
}
|
||||
|
||||
// 提交按钮
|
||||
const submitForm = async () => {
|
||||
loading.value = true
|
||||
// 提交请求
|
||||
try {
|
||||
const data = unref(formRef)?.formModel as ErrorCodeVO
|
||||
if (actionType.value === 'create') {
|
||||
await ErrorCodeApi.createErrorCodeApi(data)
|
||||
ElMessage.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await ErrorCodeApi.updateErrorCodeApi(data)
|
||||
ElMessage.success(t('common.updateSuccess'))
|
||||
}
|
||||
// 操作成功,重新加载列表
|
||||
dialogVisible.value = false
|
||||
await getList()
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 删除操作
|
||||
const handleDelete = (row: ErrorCodeVO) => {
|
||||
delList(row.id, false)
|
||||
}
|
||||
|
||||
// ========== 详情相关 ==========
|
||||
const detailRef = ref() // 详情 Ref
|
||||
|
||||
// 详情操作
|
||||
const handleDetail = async (row: ErrorCodeVO) => {
|
||||
// 设置数据
|
||||
detailRef.value = row
|
||||
setDialogTile('detail')
|
||||
}
|
||||
|
||||
// ========== 初始化 ==========
|
||||
getList()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 搜索工作区 -->
|
||||
<ContentWrap>
|
||||
<Search :schema="allSchemas.searchSchema" @search="setSearchParams" @reset="setSearchParams" />
|
||||
</ContentWrap>
|
||||
<ContentWrap>
|
||||
<!-- 操作工具栏 -->
|
||||
<div class="mb-10px">
|
||||
<el-button v-hasPermi="['system:error-code:create']" type="primary" @click="handleCreate">
|
||||
<Icon icon="el:zoom-in" class="mr-5px" /> {{ t('action.add') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<!-- 列表 -->
|
||||
<Table
|
||||
:columns="allSchemas.tableColumns"
|
||||
:selection="false"
|
||||
:data="tableObject.tableList"
|
||||
:loading="tableObject.loading"
|
||||
:pagination="{
|
||||
total: tableObject.total
|
||||
}"
|
||||
v-model:pageSize="tableObject.pageSize"
|
||||
v-model:currentPage="tableObject.currentPage"
|
||||
@register="register"
|
||||
>
|
||||
<template #type="{ row }">
|
||||
<DictTag :type="DICT_TYPE.SYSTEM_ERROR_CODE_TYPE" :value="row.type" />
|
||||
</template>
|
||||
<template #createTime="{ row }">
|
||||
<span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:error-code:update']"
|
||||
@click="handleUpdate(row)"
|
||||
>
|
||||
<Icon icon="ep:edit" class="mr-5px" /> {{ t('action.edit') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:error-code:update']"
|
||||
@click="handleDetail(row)"
|
||||
>
|
||||
<Icon icon="ep:view" class="mr-5px" /> {{ t('action.detail') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:error-code:delete']"
|
||||
@click="handleDelete(row)"
|
||||
>
|
||||
<Icon icon="ep:delete" class="mr-5px" /> {{ t('action.del') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</Table>
|
||||
</ContentWrap>
|
||||
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle">
|
||||
<!-- 对话框(添加 / 修改) -->
|
||||
<Form
|
||||
v-if="['create', 'update'].includes(actionType)"
|
||||
:schema="allSchemas.formSchema"
|
||||
:rules="rules"
|
||||
ref="formRef"
|
||||
/>
|
||||
<!-- 对话框(详情) -->
|
||||
<Descriptions
|
||||
v-if="actionType === 'detail'"
|
||||
:schema="allSchemas.detailSchema"
|
||||
:data="detailRef"
|
||||
>
|
||||
<template #type="{ row }">
|
||||
<DictTag :type="DICT_TYPE.SYSTEM_ERROR_CODE_TYPE" :value="row.type" />
|
||||
</template>
|
||||
<template #createTime="{ row }">
|
||||
<span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
</template>
|
||||
</Descriptions>
|
||||
<!-- 操作按钮 -->
|
||||
<template #footer>
|
||||
<el-button
|
||||
v-if="['create', 'update'].includes(actionType)"
|
||||
type="primary"
|
||||
:loading="loading"
|
||||
@click="submitForm"
|
||||
>
|
||||
{{ t('action.save') }}
|
||||
</el-button>
|
||||
<el-button @click="dialogVisible = false">{{ t('dialog.close') }}</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
80
yudao-ui-admin-vue3/src/views/system/loginlog/index.vue
Normal file
80
yudao-ui-admin-vue3/src/views/system/loginlog/index.vue
Normal file
@@ -0,0 +1,80 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import dayjs from 'dayjs'
|
||||
import { useTable } from '@/hooks/web/useTable'
|
||||
import { allSchemas } from './loginLog.data'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import type { LoginLogVO } from '@/api/system/loginLog/types'
|
||||
import { getLoginLogPageApi, exportLoginLogApi } from '@/api/system/loginLog'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
// ========== 列表相关 ==========
|
||||
const { register, tableObject, methods } = useTable<PageResult<LoginLogVO>, LoginLogVO>({
|
||||
getListApi: getLoginLogPageApi,
|
||||
exportListApi: exportLoginLogApi
|
||||
})
|
||||
const { getList, setSearchParams } = methods
|
||||
// 详情操作
|
||||
const dialogVisible = ref(false) // 是否显示弹出层
|
||||
const dialogTitle = ref(t('action.detail')) // 弹出层标题
|
||||
const detailRef = ref() // 详情 Ref
|
||||
const handleDetail = async (row: LoginLogVO) => {
|
||||
// 设置数据
|
||||
detailRef.value = row
|
||||
dialogVisible.value = true
|
||||
}
|
||||
getList()
|
||||
</script>
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<Search :schema="allSchemas.searchSchema" @search="setSearchParams" @reset="setSearchParams" />
|
||||
</ContentWrap>
|
||||
<ContentWrap>
|
||||
<Table
|
||||
:columns="allSchemas.tableColumns"
|
||||
:selection="false"
|
||||
:data="tableObject.tableList"
|
||||
:loading="tableObject.loading"
|
||||
:pagination="{
|
||||
total: tableObject.total
|
||||
}"
|
||||
v-model:pageSize="tableObject.pageSize"
|
||||
v-model:currentPage="tableObject.currentPage"
|
||||
@register="register"
|
||||
>
|
||||
<template #logType="{ row }">
|
||||
<DictTag :type="DICT_TYPE.SYSTEM_LOGIN_TYPE" :value="row.logType" />
|
||||
</template>
|
||||
<template #result="{ row }">
|
||||
<DictTag :type="DICT_TYPE.SYSTEM_LOGIN_RESULT" :value="row.result" />
|
||||
</template>
|
||||
<template #createTime="{ row }">
|
||||
<span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<el-button link type="primary" @click="handleDetail(row)">
|
||||
<Icon icon="ep:view" class="mr-5px" /> {{ t('action.detail') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</Table>
|
||||
</ContentWrap>
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle" maxHeight="500px" width="50%">
|
||||
<!-- 对话框(详情) -->
|
||||
<Descriptions :schema="allSchemas.detailSchema" :data="detailRef">
|
||||
<template #logType="{ row }">
|
||||
<DictTag :type="DICT_TYPE.SYSTEM_LOGIN_TYPE" :value="row.logType" />
|
||||
</template>
|
||||
<template #result="{ row }">
|
||||
<DictTag :type="DICT_TYPE.SYSTEM_LOGIN_RESULT" :value="row.result" />
|
||||
</template>
|
||||
<template #createTime="{ row }">
|
||||
<span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
</template>
|
||||
</Descriptions>
|
||||
<!-- 操作按钮 -->
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">{{ t('dialog.close') }}</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
@@ -0,0 +1,92 @@
|
||||
import { reactive } from 'vue'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { CrudSchema, useCrudSchemas } from '@/hooks/web/useCrudSchemas'
|
||||
const { t } = useI18n() // 国际化
|
||||
const crudSchemas = reactive<CrudSchema[]>([
|
||||
{
|
||||
label: t('common.index'),
|
||||
field: 'id',
|
||||
type: 'index',
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '日志类型',
|
||||
field: 'logType',
|
||||
dictType: DICT_TYPE.SYSTEM_LOGIN_TYPE,
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '用户名称',
|
||||
field: 'username',
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '登录地址',
|
||||
field: 'userIp',
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'userAgent',
|
||||
field: 'userAgent'
|
||||
},
|
||||
{
|
||||
label: '登陆结果',
|
||||
field: 'result',
|
||||
dictType: DICT_TYPE.SYSTEM_LOGIN_RESULT,
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('common.createTime'),
|
||||
field: 'createTime',
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('common.createTime'),
|
||||
field: 'daterange',
|
||||
table: {
|
||||
show: false
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
},
|
||||
search: {
|
||||
show: true,
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
type: 'daterange',
|
||||
valueFormat: 'YYYY-MM-DD'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('table.action'),
|
||||
field: 'action',
|
||||
width: '80px',
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
])
|
||||
export const { allSchemas } = useCrudSchemas(crudSchemas)
|
404
yudao-ui-admin-vue3/src/views/system/menu/index.vue
Normal file
404
yudao-ui-admin-vue3/src/views/system/menu/index.vue
Normal file
@@ -0,0 +1,404 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { handleTree } from '@/utils/tree'
|
||||
import dayjs from 'dayjs'
|
||||
import { IconSelect } from '@/components/Icon'
|
||||
import { Tooltip } from '@/components/Tooltip'
|
||||
import * as MenuApi from '@/api/system/menu'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import {
|
||||
ElRow,
|
||||
ElCol,
|
||||
ElTable,
|
||||
ElTableColumn,
|
||||
ElForm,
|
||||
ElFormItem,
|
||||
ElInput,
|
||||
ElInputNumber,
|
||||
ElSelect,
|
||||
ElOption,
|
||||
ElMessageBox,
|
||||
ElMessage,
|
||||
ElCascader,
|
||||
ElRadioGroup,
|
||||
ElRadioButton
|
||||
} from 'element-plus'
|
||||
import { MenuVO } from '@/api/system/menu/types'
|
||||
import { SystemMenuTypeEnum, CommonStatusEnum } from '@/utils/constants'
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
const { t } = useI18n() // 国际化
|
||||
// ========== 创建菜单树结构 ==========
|
||||
const loading = ref(true)
|
||||
const menuData = ref([]) // 树形结构
|
||||
const getList = async () => {
|
||||
const res = await MenuApi.getMenuListApi(queryParams)
|
||||
menuData.value = handleTree(res)
|
||||
loading.value = false
|
||||
}
|
||||
const menuProps = {
|
||||
checkStrictly: true,
|
||||
children: 'children',
|
||||
label: 'name',
|
||||
value: 'id'
|
||||
}
|
||||
const menuOptions = ref([]) // 树形结构
|
||||
const getTree = async () => {
|
||||
const res = await MenuApi.listSimpleMenusApi()
|
||||
menuOptions.value = handleTree(res)
|
||||
}
|
||||
// ========== 查询 ==========
|
||||
const queryParams = reactive({
|
||||
name: undefined,
|
||||
status: undefined
|
||||
})
|
||||
// 查询操作
|
||||
const handleQuery = async () => {
|
||||
await getList()
|
||||
}
|
||||
// 重置操作
|
||||
const resetQuery = async () => {
|
||||
queryParams.name = undefined
|
||||
queryParams.status = undefined
|
||||
await getList()
|
||||
}
|
||||
// ========== CRUD 相关 ==========
|
||||
const actionLoading = ref(false) // 遮罩层
|
||||
const actionType = ref('') // 操作按钮的类型
|
||||
const dialogVisible = ref(false) // 是否显示弹出层
|
||||
const dialogTitle = ref('edit') // 弹出层标题
|
||||
const menuForm = ref<MenuVO>({
|
||||
id: 0,
|
||||
name: '',
|
||||
permission: '',
|
||||
type: SystemMenuTypeEnum.DIR,
|
||||
sort: 1,
|
||||
parentId: 0,
|
||||
path: '',
|
||||
icon: '',
|
||||
component: '',
|
||||
status: CommonStatusEnum.ENABLE,
|
||||
visible: true,
|
||||
keepAlive: true,
|
||||
createTime: ''
|
||||
})
|
||||
// 表单校验
|
||||
const rules = {
|
||||
name: [{ required: true, message: '菜单名称不能为空', trigger: 'blur' }],
|
||||
sort: [{ required: true, message: '菜单顺序不能为空', trigger: 'blur' }],
|
||||
path: [{ required: true, message: '路由地址不能为空', trigger: 'blur' }],
|
||||
status: [{ required: true, message: '状态不能为空', trigger: 'blur' }]
|
||||
}
|
||||
// 设置标题
|
||||
const setDialogTile = (type: string) => {
|
||||
dialogTitle.value = t('action.' + type)
|
||||
actionType.value = type
|
||||
dialogVisible.value = true
|
||||
}
|
||||
// 新建操作
|
||||
const handleCreate = () => {
|
||||
// 重置表单
|
||||
setDialogTile('create')
|
||||
}
|
||||
// 修改操作
|
||||
const handleUpdate = async (row: MenuVO) => {
|
||||
// 设置数据
|
||||
const res = await MenuApi.getMenuApi(row.id)
|
||||
menuForm.value = res
|
||||
setDialogTile('update')
|
||||
}
|
||||
// 删除操作
|
||||
const handleDelete = async (row: MenuVO) => {
|
||||
ElMessageBox.confirm(t('common.delDataMessage'), t('common.confirmTitle'), {
|
||||
confirmButtonText: t('common.ok'),
|
||||
cancelButtonText: t('common.cancel'),
|
||||
type: 'warning'
|
||||
})
|
||||
.then(async () => {
|
||||
await MenuApi.deleteMenuApi(row.id)
|
||||
ElMessage.success(t('common.delSuccess'))
|
||||
})
|
||||
.catch(() => {})
|
||||
await getList()
|
||||
}
|
||||
// 保存操作
|
||||
function isExternal(path: string) {
|
||||
return /^(https?:|mailto:|tel:)/.test(path)
|
||||
}
|
||||
const submitForm = async () => {
|
||||
actionLoading.value = true
|
||||
// 提交请求
|
||||
try {
|
||||
if (
|
||||
menuForm.value.type === SystemMenuTypeEnum.DIR ||
|
||||
menuForm.value.type === SystemMenuTypeEnum.MENU
|
||||
) {
|
||||
if (!isExternal(menuForm.value.path)) {
|
||||
if (menuForm.value.parentId === 0 && menuForm.value.path.charAt(0) !== '/') {
|
||||
ElMessage.error('路径必须以 / 开头')
|
||||
return
|
||||
} else if (menuForm.value.parentId !== 0 && menuForm.value.path.charAt(0) === '/') {
|
||||
ElMessage.error('路径不能以 / 开头')
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
if (actionType.value === 'create') {
|
||||
await MenuApi.createMenuApi(menuForm.value)
|
||||
ElMessage.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await MenuApi.updateMenuApi(menuForm.value)
|
||||
ElMessage.success(t('common.updateSuccess'))
|
||||
}
|
||||
// 操作成功,重新加载列表
|
||||
dialogVisible.value = false
|
||||
await getList()
|
||||
} finally {
|
||||
actionLoading.value = false
|
||||
}
|
||||
}
|
||||
// ========== 初始化 ==========
|
||||
onMounted(async () => {
|
||||
await getList()
|
||||
getTree()
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<!-- 搜索工作区 -->
|
||||
<ContentWrap>
|
||||
<el-form :model="queryParams" ref="queryForm" :inline="true">
|
||||
<el-form-item label="菜单名称" prop="name">
|
||||
<el-input v-model="queryParams.name" placeholder="请输入菜单名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择菜单状态">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleQuery">
|
||||
<Icon icon="ep:search" class="mr-5px" />
|
||||
{{ t('common.query') }}
|
||||
</el-button>
|
||||
<el-button @click="resetQuery">
|
||||
<Icon icon="ep:refresh-right" class="mr-5px" />
|
||||
{{ t('common.reset') }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<div class="mb-10px">
|
||||
<el-button type="primary" v-hasPermi="['system:notice:create']" @click="handleCreate">
|
||||
<Icon icon="el:zoom-in" class="mr-5px" /> {{ t('action.add') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
table-layout="auto"
|
||||
row-key="id"
|
||||
:data="menuData"
|
||||
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
|
||||
>
|
||||
<el-table-column label="菜单名称" prop="name" width="240px">
|
||||
<template #default="scope">
|
||||
<Icon :icon="scope.row.icon" />
|
||||
<span class="ml-3">{{ scope.row.name }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="菜单类型" prop="type">
|
||||
<template #default="scope">
|
||||
<DictTag :type="DICT_TYPE.SYSTEM_MENU_TYPE" :value="scope.row.type" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="路由地址" prop="path" />
|
||||
<el-table-column label="组件路径" prop="component" />
|
||||
<el-table-column label="权限标识" prop="permission" />
|
||||
<el-table-column label="排序" prop="sort" />
|
||||
<el-table-column label="状态" prop="status">
|
||||
<template #default="scope">
|
||||
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" prop="createTime">
|
||||
<template #default="scope">
|
||||
<span>{{ dayjs(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:menu:update']"
|
||||
@click="handleUpdate(scope.row)"
|
||||
>
|
||||
<Icon icon="ep:edit" class="mr-5px" /> {{ t('action.edit') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:menu:delete']"
|
||||
@click="handleDelete(scope.row)"
|
||||
>
|
||||
<Icon icon="ep:delete" class="mr-5px" /> {{ t('action.del') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</ContentWrap>
|
||||
<!-- 添加或修改菜单对话框 -->
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle" maxHeight="400px" width="40%">
|
||||
<el-form
|
||||
:model="menuForm"
|
||||
:rules="rules"
|
||||
:inline="true"
|
||||
label-width="120px"
|
||||
label-position="right"
|
||||
>
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="上级菜单">
|
||||
<el-cascader
|
||||
:options="menuData"
|
||||
:props="menuProps"
|
||||
placeholder="请选择上级菜单"
|
||||
v-model="menuForm.parentId"
|
||||
class="w-100"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="菜单类型" prop="type">
|
||||
<el-radio-group v-model="menuForm.type">
|
||||
<el-radio-button
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_MENU_TYPE)"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="菜单名称" prop="name">
|
||||
<el-input v-model="menuForm.name" placeholder="请输入菜单名称" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<template v-if="menuForm.type !== 3">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="菜单图标">
|
||||
<IconSelect v-model="menuForm.icon" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="路由地址" prop="path">
|
||||
<template #label>
|
||||
<Tooltip
|
||||
titel="路由地址"
|
||||
message="访问的路由地址,如:`user`。如需外网地址时,则以 `http(s)://` 开头"
|
||||
/>
|
||||
</template>
|
||||
<el-input v-model="menuForm.path" placeholder="请输入路由地址" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</template>
|
||||
<template v-if="menuForm.type === 2">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="路由地址" prop="component">
|
||||
<el-input v-model="menuForm.component" placeholder="请输入组件地址" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</template>
|
||||
<template v-if="menuForm.type !== 1">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="权限标识" prop="permission">
|
||||
<template #label>
|
||||
<Tooltip
|
||||
titel="权限标识"
|
||||
message="Controller 方法上的权限字符,如:@PreAuthorize(`@ss.hasPermission('system:user:list')`)"
|
||||
/>
|
||||
</template>
|
||||
<el-input v-model="menuForm.permission" placeholder="请输入权限标识" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</template>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="显示排序" prop="sort">
|
||||
<el-input-number v-model="menuForm.sort" controls-position="right" :min="0" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="菜单状态" prop="status">
|
||||
<el-radio-group v-model="menuForm.status">
|
||||
<el-radio-button
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<template v-if="menuForm.type !== 3">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="显示状态" prop="status">
|
||||
<template #label>
|
||||
<Tooltip
|
||||
titel="显示状态"
|
||||
message="选择隐藏时,路由将不会出现在侧边栏,但仍然可以访问"
|
||||
/>
|
||||
</template>
|
||||
<el-radio-group v-model="menuForm.visible">
|
||||
<el-radio-button key="true" :label="true">显示</el-radio-button>
|
||||
<el-radio-button key="false" :label="false">隐藏</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</template>
|
||||
<template v-if="menuForm.type === 2">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="缓存状态" prop="keepAlive">
|
||||
<template #label>
|
||||
<Tooltip
|
||||
titel="缓存状态"
|
||||
message="选择缓存时,则会被 `keep-alive` 缓存,需要匹配组件的 `name` 和路由地址保持一致"
|
||||
/>
|
||||
</template>
|
||||
<el-radio-group v-model="menuForm.keepAlive">
|
||||
<el-radio-button key="true" :label="true">缓存</el-radio-button>
|
||||
<el-radio-button key="false" :label="false">不缓存</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<!-- 操作按钮 -->
|
||||
<template #footer>
|
||||
<el-button
|
||||
v-if="['create', 'update'].includes(actionType)"
|
||||
type="primary"
|
||||
:loading="actionLoading"
|
||||
@click="submitForm"
|
||||
>
|
||||
{{ t('action.save') }}
|
||||
</el-button>
|
||||
<el-button @click="dialogVisible = false">{{ t('dialog.close') }}</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<style lang="less" scoped>
|
||||
:deep(.el-button.is-text) {
|
||||
margin-left: 0;
|
||||
padding: 8px 10px;
|
||||
}
|
||||
</style>
|
166
yudao-ui-admin-vue3/src/views/system/menu/menu.data.ts
Normal file
166
yudao-ui-admin-vue3/src/views/system/menu/menu.data.ts
Normal file
@@ -0,0 +1,166 @@
|
||||
import { reactive } from 'vue'
|
||||
import { required } from '@/utils/formRules'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
// 国际化
|
||||
const { t } = useI18n()
|
||||
// 修改
|
||||
export const modelSchema = reactive<FormSchema[]>([
|
||||
{
|
||||
label: '上级菜单',
|
||||
field: 'parentId',
|
||||
component: 'Input',
|
||||
formItemProps: {
|
||||
rules: [required]
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '菜单类型',
|
||||
field: 'type',
|
||||
component: 'RadioButton',
|
||||
formItemProps: {
|
||||
rules: [required]
|
||||
},
|
||||
componentProps: {
|
||||
options: [
|
||||
{
|
||||
label: '目录',
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
label: '菜单',
|
||||
value: 2
|
||||
},
|
||||
{
|
||||
label: '按钮',
|
||||
value: 3
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '菜单图标',
|
||||
field: 'icon',
|
||||
component: 'Input',
|
||||
formItemProps: {
|
||||
rules: [required]
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '菜单名称',
|
||||
field: 'name',
|
||||
component: 'Input',
|
||||
formItemProps: {
|
||||
rules: [required]
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '显示排序',
|
||||
field: 'sort',
|
||||
component: 'Input'
|
||||
},
|
||||
{
|
||||
label: '路由地址',
|
||||
field: 'path',
|
||||
component: 'Input',
|
||||
labelMessage: '访问的路由地址,如:`user`。如需外网地址时,则以 `http(s)://` 开头'
|
||||
},
|
||||
{
|
||||
label: '组件路径',
|
||||
field: 'component',
|
||||
component: 'Input'
|
||||
},
|
||||
{
|
||||
label: '权限标识',
|
||||
field: 'permission',
|
||||
component: 'Input',
|
||||
labelMessage:
|
||||
'Controller 方法上的权限字符,如:@PreAuthorize(`@ss.hasPermission(`system:user:list`)`)'
|
||||
},
|
||||
{
|
||||
label: t('common.status'),
|
||||
field: 'status',
|
||||
component: 'RadioButton',
|
||||
value: 0,
|
||||
formItemProps: {
|
||||
rules: [required]
|
||||
},
|
||||
componentProps: {
|
||||
options: [
|
||||
{
|
||||
label: '开启',
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
label: '关闭',
|
||||
value: 1
|
||||
}
|
||||
]
|
||||
},
|
||||
labelMessage: '选择停用时,路由将不会出现在侧边栏,也不能被访问'
|
||||
},
|
||||
{
|
||||
label: '是否显示',
|
||||
field: 'visible',
|
||||
component: 'RadioButton',
|
||||
value: 0,
|
||||
formItemProps: {
|
||||
rules: [required]
|
||||
},
|
||||
componentProps: {
|
||||
options: [
|
||||
{
|
||||
label: '显示',
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
label: '隐藏',
|
||||
value: 1
|
||||
}
|
||||
]
|
||||
},
|
||||
labelMessage: '选择隐藏时,路由将不会出现在侧边栏,但仍然可以访问'
|
||||
},
|
||||
{
|
||||
label: '是否缓存',
|
||||
field: 'keepAlive',
|
||||
component: 'RadioButton',
|
||||
value: 0,
|
||||
componentProps: {
|
||||
options: [
|
||||
{
|
||||
label: '缓存',
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
label: '不缓存',
|
||||
value: 1
|
||||
}
|
||||
]
|
||||
},
|
||||
labelMessage: '选择缓存时,则会被 `keep-alive` 缓存,需要匹配组件的 `name` 和路由地址保持一致'
|
||||
}
|
||||
])
|
||||
// 列表
|
||||
export const columns = reactive<TableColumn[]>([
|
||||
{
|
||||
label: '菜单名称',
|
||||
field: 'name'
|
||||
},
|
||||
{
|
||||
label: '权限标识',
|
||||
field: 'permission',
|
||||
component: 'Input',
|
||||
formItemProps: {
|
||||
rules: [required]
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '排序',
|
||||
field: 'sort'
|
||||
},
|
||||
{
|
||||
label: t('table.action'),
|
||||
field: 'action',
|
||||
width: '180px'
|
||||
}
|
||||
])
|
226
yudao-ui-admin-vue3/src/views/system/menu/old.vue
Normal file
226
yudao-ui-admin-vue3/src/views/system/menu/old.vue
Normal file
@@ -0,0 +1,226 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, unref } from 'vue'
|
||||
import { handleTree } from '@/utils/tree'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { IconSelect } from '@/components/Icon'
|
||||
import { ElCard, ElMessage, ElMessageBox, ElTree, ElTreeSelect } from 'element-plus'
|
||||
import { columns, modelSchema } from './menu.data'
|
||||
import { Form, FormExpose } from '@/components/Form'
|
||||
import * as MenuApi from '@/api/system/menu'
|
||||
import { MenuVO } from '@/api/system/menu/types'
|
||||
const { t } = useI18n() // 国际化
|
||||
interface Tree {
|
||||
id: number
|
||||
name: string
|
||||
children?: Tree[]
|
||||
}
|
||||
const defaultProps = {
|
||||
children: 'children',
|
||||
label: 'name',
|
||||
value: 'id'
|
||||
}
|
||||
// ========== 创建菜单树结构 ==========
|
||||
const menuOptions = ref([]) // 树形结构
|
||||
const treeRef = ref<InstanceType<typeof ElTree>>()
|
||||
const getTree = async () => {
|
||||
const res = await MenuApi.listSimpleMenusApi()
|
||||
menuOptions.value = handleTree(res)
|
||||
}
|
||||
const filterNode = (value: string, data: Tree) => {
|
||||
if (!value) return true
|
||||
return data.name.includes(value)
|
||||
}
|
||||
// ========== 菜单信息form表单 ==========
|
||||
const loading = ref(false) // 遮罩层
|
||||
const formRef = ref<FormExpose>()
|
||||
const iconModel = ref('ep:user')
|
||||
const menuParentId = ref()
|
||||
const menuStatus = ref('add')
|
||||
const menuTitle = ref('菜单信息')
|
||||
const showEdit = ref(false)
|
||||
// 提交按钮
|
||||
const submitForm = async () => {
|
||||
loading.value = true
|
||||
// 提交请求
|
||||
try {
|
||||
const data = unref(formRef)?.formModel as MenuVO
|
||||
data.parentId = menuParentId.value
|
||||
// TODO: 表单提交待完善
|
||||
if (menuStatus.value === 'add') {
|
||||
await MenuApi.createMenuApi(data)
|
||||
} else if (menuStatus.value === 'edit') {
|
||||
await MenuApi.updateMenuApi(data)
|
||||
}
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
// ========== 按钮列表相关 ==========
|
||||
const tableData = ref([])
|
||||
const tableLoading = ref(false)
|
||||
const onDisabled = ref(true)
|
||||
const tableTitle = ref('按钮信息')
|
||||
// 树点击事件
|
||||
const handleMenuNodeClick = async (data: { [key: string]: any }) => {
|
||||
showEdit.value = true
|
||||
const res = await MenuApi.getMenuApi(data.id)
|
||||
menuTitle.value = res.name + '-菜单信息'
|
||||
tableTitle.value = res.name + '-按钮列表'
|
||||
menuParentId.value = data.id
|
||||
tableData.value = await MenuApi.getMenuListApi({ name: res.name })
|
||||
unref(formRef)?.setValues(res)
|
||||
onDisabled.value = true
|
||||
changeDisabled()
|
||||
}
|
||||
const handleCreate = () => {
|
||||
// 重置表单
|
||||
unref(formRef)?.getElFormRef()?.resetFields()
|
||||
menuParentId.value = 0
|
||||
onDisabled.value = false
|
||||
changeDisabled()
|
||||
}
|
||||
const handleEdit = () => {
|
||||
onDisabled.value = false
|
||||
changeDisabled()
|
||||
}
|
||||
const changeDisabled = () => {
|
||||
unref(formRef)?.setProps({
|
||||
disabled: onDisabled
|
||||
})
|
||||
}
|
||||
// 修改操作
|
||||
const handleUpdate = async (row: MenuVO) => {
|
||||
// 设置数据
|
||||
const res = await MenuApi.getMenuApi(row.id)
|
||||
unref(formRef)?.setValues(res)
|
||||
}
|
||||
|
||||
// 删除操作
|
||||
const handleDelete = (row: MenuVO) => {
|
||||
ElMessageBox.confirm(t('common.delDataMessage'), t('common.confirmTitle'), {
|
||||
confirmButtonText: t('common.ok'),
|
||||
cancelButtonText: t('common.cancel'),
|
||||
type: 'warning'
|
||||
})
|
||||
.then(async () => {
|
||||
await MenuApi.deleteMenuApi(row.id)
|
||||
ElMessage.success(t('common.delSuccess'))
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
onMounted(async () => {
|
||||
await getTree()
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<div class="flex">
|
||||
<el-card class="w-1/4 menu" :gutter="12" shadow="always">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>菜单列表</span>
|
||||
<el-button type="primary" v-hasPermi="['system:menu:create']" @click="handleCreate">
|
||||
新增根节点
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<!-- <p>菜单列表</p> -->
|
||||
<el-tree
|
||||
ref="treeRef"
|
||||
node-key="id"
|
||||
:accordion="true"
|
||||
:data="menuOptions"
|
||||
:props="defaultProps"
|
||||
:highlight-current="true"
|
||||
:filter-method="filterNode"
|
||||
@node-click="handleMenuNodeClick"
|
||||
/>
|
||||
</el-card>
|
||||
<el-card class="w-1/2 menu" style="margin-left: 10px" :gutter="12" shadow="hover">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>{{ menuTitle }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<div v-if="!showEdit">
|
||||
<span>请从左侧选择菜单</span>
|
||||
</div>
|
||||
<div v-if="showEdit">
|
||||
<Form :loading="loading" :schema="modelSchema" ref="formRef">
|
||||
<template #parentId>
|
||||
<el-tree-select
|
||||
node-key="id"
|
||||
v-model="menuParentId"
|
||||
:props="defaultProps"
|
||||
:data="menuOptions"
|
||||
check-strictly
|
||||
/>
|
||||
</template>
|
||||
<template #icon>
|
||||
<IconSelect v-model="iconModel" />
|
||||
</template>
|
||||
</Form>
|
||||
<el-button
|
||||
v-if="!onDisabled"
|
||||
type="primary"
|
||||
v-hasPermi="['system:menu:update']"
|
||||
:loading="loading"
|
||||
@click="submitForm"
|
||||
>
|
||||
{{ t('action.save') }}
|
||||
</el-button>
|
||||
<el-button v-if="!onDisabled" :loading="loading" @click="showEdit = false">
|
||||
{{ t('common.cancel') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="onDisabled"
|
||||
v-hasPermi="['system:menu:update']"
|
||||
type="primary"
|
||||
:loading="loading"
|
||||
@click="handleEdit"
|
||||
>
|
||||
{{ t('action.edit') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card class="w-1/2 menu" style="margin-left: 10px" :gutter="12" shadow="hover">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>{{ tableTitle }}</span>
|
||||
<!-- <el-button type="primary">新增根节点</el-button> -->
|
||||
</div>
|
||||
</template>
|
||||
<!-- 列表 -->
|
||||
<Table :loading="tableLoading" :columns="columns" :data="tableData">
|
||||
<template #action="{ row }">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:menu:update']"
|
||||
@click="handleUpdate(row)"
|
||||
>
|
||||
<Icon icon="ep:edit" class="mr-5px" /> {{ t('action.edit') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:menu:delete']"
|
||||
@click="handleDelete(row)"
|
||||
>
|
||||
<Icon icon="ep:delete" class="mr-5px" /> {{ t('action.del') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</Table>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
<style scoped>
|
||||
.menu {
|
||||
height: 1000px;
|
||||
max-height: 1800px;
|
||||
}
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
189
yudao-ui-admin-vue3/src/views/system/notice/index.vue
Normal file
189
yudao-ui-admin-vue3/src/views/system/notice/index.vue
Normal file
@@ -0,0 +1,189 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, unref } from 'vue'
|
||||
import dayjs from 'dayjs'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { useTable } from '@/hooks/web/useTable'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { FormExpose } from '@/components/Form'
|
||||
import type { NoticeVO } from '@/api/system/notice/types'
|
||||
import { rules, allSchemas } from './notice.data'
|
||||
import * as NoticeApi from '@/api/system/notice'
|
||||
const { t } = useI18n() // 国际化
|
||||
// ========== 列表相关 ==========
|
||||
const { register, tableObject, methods } = useTable<PageResult<NoticeVO>, NoticeVO>({
|
||||
getListApi: NoticeApi.getNoticePageApi,
|
||||
delListApi: NoticeApi.deleteNoticeApi
|
||||
})
|
||||
const { getList, setSearchParams, delList } = methods
|
||||
|
||||
// ========== CRUD 相关 ==========
|
||||
const actionLoading = ref(false) // 遮罩层
|
||||
const actionType = ref('') // 操作按钮的类型
|
||||
const dialogVisible = ref(false) // 是否显示弹出层
|
||||
const dialogTitle = ref('edit') // 弹出层标题
|
||||
const formRef = ref<FormExpose>() // 表单 Ref
|
||||
|
||||
// 设置标题
|
||||
const setDialogTile = (type: string) => {
|
||||
dialogTitle.value = t('action.' + type)
|
||||
actionType.value = type
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
// 新增操作
|
||||
const handleCreate = () => {
|
||||
setDialogTile('create')
|
||||
// 重置表单
|
||||
unref(formRef)?.getElFormRef()?.resetFields()
|
||||
}
|
||||
|
||||
// 修改操作
|
||||
const handleUpdate = async (row: NoticeVO) => {
|
||||
setDialogTile('update')
|
||||
// 设置数据
|
||||
const res = await NoticeApi.getNoticeApi(row.id)
|
||||
unref(formRef)?.setValues(res)
|
||||
}
|
||||
|
||||
// 提交按钮
|
||||
const submitForm = async () => {
|
||||
actionLoading.value = true
|
||||
// 提交请求
|
||||
try {
|
||||
const data = unref(formRef)?.formModel as NoticeVO
|
||||
if (actionType.value === 'create') {
|
||||
await NoticeApi.createNoticeApi(data)
|
||||
ElMessage.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await NoticeApi.updateNoticeApi(data)
|
||||
ElMessage.success(t('common.updateSuccess'))
|
||||
}
|
||||
// 操作成功,重新加载列表
|
||||
dialogVisible.value = false
|
||||
await getList()
|
||||
} finally {
|
||||
actionLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 删除操作
|
||||
const handleDelete = (row: NoticeVO) => {
|
||||
delList(row.id, false)
|
||||
}
|
||||
|
||||
// ========== 详情相关 ==========
|
||||
const detailRef = ref() // 详情 Ref
|
||||
|
||||
// 详情操作
|
||||
const handleDetail = async (row: NoticeVO) => {
|
||||
// 设置数据
|
||||
detailRef.value = row
|
||||
setDialogTile('detail')
|
||||
}
|
||||
|
||||
// ========== 初始化 ==========
|
||||
getList()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 搜索工作区 -->
|
||||
<ContentWrap>
|
||||
<Search :schema="allSchemas.searchSchema" @search="setSearchParams" @reset="setSearchParams" />
|
||||
</ContentWrap>
|
||||
<ContentWrap>
|
||||
<!-- 操作工具栏 -->
|
||||
<div class="mb-10px">
|
||||
<el-button type="primary" v-hasPermi="['system:notice:create']" @click="handleCreate">
|
||||
<Icon icon="el:zoom-in" class="mr-5px" /> {{ t('action.add') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<!-- 列表 -->
|
||||
<Table
|
||||
:columns="allSchemas.tableColumns"
|
||||
:selection="false"
|
||||
:data="tableObject.tableList"
|
||||
:loading="tableObject.loading"
|
||||
:pagination="{
|
||||
total: tableObject.total
|
||||
}"
|
||||
v-model:pageSize="tableObject.pageSize"
|
||||
v-model:currentPage="tableObject.currentPage"
|
||||
@register="register"
|
||||
>
|
||||
<template #type="{ row }">
|
||||
<DictTag :type="DICT_TYPE.SYSTEM_NOTICE_TYPE" :value="row.type" />
|
||||
</template>
|
||||
<template #status="{ row }">
|
||||
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
|
||||
</template>
|
||||
<template #createTime="{ row }">
|
||||
<span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:notice:update']"
|
||||
@click="handleUpdate(row)"
|
||||
>
|
||||
<Icon icon="ep:edit" class="mr-5px" /> {{ t('action.edit') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:notice:update']"
|
||||
@click="handleDetail(row)"
|
||||
>
|
||||
<Icon icon="ep:view" class="mr-5px" /> {{ t('action.detail') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:notice:delete']"
|
||||
@click="handleDelete(row)"
|
||||
>
|
||||
<Icon icon="ep:delete" class="mr-5px" /> {{ t('action.del') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</Table>
|
||||
</ContentWrap>
|
||||
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle" maxHeight="500px" width="50%">
|
||||
<!-- 对话框(添加 / 修改) -->
|
||||
<Form
|
||||
v-if="['create', 'update'].includes(actionType)"
|
||||
:schema="allSchemas.formSchema"
|
||||
:rules="rules"
|
||||
ref="formRef"
|
||||
/>
|
||||
<!-- 对话框(详情) -->
|
||||
<Descriptions
|
||||
v-if="actionType === 'detail'"
|
||||
:schema="allSchemas.detailSchema"
|
||||
:data="detailRef"
|
||||
>
|
||||
<template #type="{ row }">
|
||||
<DictTag :type="DICT_TYPE.SYSTEM_NOTICE_TYPE" :value="row.type" />
|
||||
</template>
|
||||
<template #status="{ row }">
|
||||
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
|
||||
</template>
|
||||
<template #createTime="{ row }">
|
||||
<span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
</template>
|
||||
</Descriptions>
|
||||
<!-- 操作按钮 -->
|
||||
<template #footer>
|
||||
<el-button
|
||||
v-if="['create', 'update'].includes(actionType)"
|
||||
type="primary"
|
||||
:loading="actionLoading"
|
||||
@click="submitForm"
|
||||
>
|
||||
{{ t('action.save') }}
|
||||
</el-button>
|
||||
<el-button @click="dialogVisible = false">{{ t('dialog.close') }}</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
86
yudao-ui-admin-vue3/src/views/system/notice/notice.data.ts
Normal file
86
yudao-ui-admin-vue3/src/views/system/notice/notice.data.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import { reactive } from 'vue'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { required } from '@/utils/formRules'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { CrudSchema, useCrudSchemas } from '@/hooks/web/useCrudSchemas'
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
// 表单校验
|
||||
export const rules = reactive({
|
||||
title: [required],
|
||||
type: [required],
|
||||
status: [required]
|
||||
})
|
||||
|
||||
// CrudSchema
|
||||
const crudSchemas = reactive<CrudSchema[]>([
|
||||
{
|
||||
label: t('common.index'),
|
||||
field: 'id',
|
||||
type: 'index',
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '公告标题',
|
||||
field: 'title',
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '公告类型',
|
||||
field: 'type',
|
||||
dictType: DICT_TYPE.SYSTEM_NOTICE_TYPE,
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('common.status'),
|
||||
field: 'status',
|
||||
dictType: DICT_TYPE.COMMON_STATUS,
|
||||
search: {
|
||||
show: true
|
||||
},
|
||||
form: {
|
||||
component: 'RadioButton'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '公告内容',
|
||||
field: 'content',
|
||||
form: {
|
||||
component: 'Editor',
|
||||
colProps: {
|
||||
span: 24
|
||||
},
|
||||
componentProps: {
|
||||
valueHtml: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('common.createTime'),
|
||||
field: 'createTime',
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('table.action'),
|
||||
field: 'action',
|
||||
width: '240px',
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
])
|
||||
export const { allSchemas } = useCrudSchemas(crudSchemas)
|
@@ -0,0 +1,141 @@
|
||||
import { reactive } from 'vue'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { required } from '@/utils/formRules'
|
||||
import { CrudSchema, useCrudSchemas } from '@/hooks/web/useCrudSchemas'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
const { t } = useI18n()
|
||||
// 国际化
|
||||
|
||||
// 表单校验
|
||||
export const rules = reactive({
|
||||
clientId: [required],
|
||||
secret: [required],
|
||||
name: [required],
|
||||
logo: [required],
|
||||
status: [required],
|
||||
accessTokenValiditySeconds: [required],
|
||||
refreshTokenValiditySeconds: [required],
|
||||
redirectUris: [required],
|
||||
authorizedGrantTypes: [required]
|
||||
})
|
||||
|
||||
// CrudSchema
|
||||
const crudSchemas = reactive<CrudSchema[]>([
|
||||
{
|
||||
label: '客户端编号',
|
||||
field: 'clientId',
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '客户端密钥',
|
||||
field: 'secret'
|
||||
},
|
||||
{
|
||||
label: '应用名',
|
||||
field: 'name',
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '应用图标',
|
||||
field: 'logo'
|
||||
},
|
||||
{
|
||||
label: t('common.status'),
|
||||
field: 'status',
|
||||
dictType: DICT_TYPE.COMMON_STATUS,
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '访问令牌的有效期',
|
||||
field: 'accessTokenValiditySeconds'
|
||||
},
|
||||
{
|
||||
label: '刷新令牌的有效期',
|
||||
field: 'refreshTokenValiditySeconds'
|
||||
},
|
||||
{
|
||||
label: '授权类型',
|
||||
field: 'authorizedGrantTypes',
|
||||
dictType: DICT_TYPE.SYSTEM_OAUTH2_GRANT_TYPE
|
||||
},
|
||||
{
|
||||
label: '授权范围',
|
||||
field: 'scopes',
|
||||
table: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '自动授权范围',
|
||||
field: 'autoApproveScopes',
|
||||
table: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '可重定向的 URI 地址',
|
||||
field: 'redirectUris',
|
||||
table: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '权限',
|
||||
field: 'authorities',
|
||||
table: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '资源',
|
||||
field: 'resourceIds',
|
||||
table: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '附加信息',
|
||||
field: 'additionalInformation',
|
||||
table: {
|
||||
show: false
|
||||
},
|
||||
form: {
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
type: 'textarea',
|
||||
rows: 4
|
||||
},
|
||||
colProps: {
|
||||
span: 24
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('common.createTime'),
|
||||
field: 'createTime',
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('table.action'),
|
||||
field: 'action',
|
||||
width: '240px',
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
])
|
||||
export const { allSchemas } = useCrudSchemas(crudSchemas)
|
187
yudao-ui-admin-vue3/src/views/system/oauth2/client/index.vue
Normal file
187
yudao-ui-admin-vue3/src/views/system/oauth2/client/index.vue
Normal file
@@ -0,0 +1,187 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, unref } from 'vue'
|
||||
import dayjs from 'dayjs'
|
||||
import { ElMessage, ElImage } from 'element-plus'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { useTable } from '@/hooks/web/useTable'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { FormExpose } from '@/components/Form'
|
||||
import type { OAuth2ClientVo } from '@/api/system/oauth2/client.types'
|
||||
import { rules, allSchemas } from './client.data'
|
||||
import * as ClientApi from '@/api/system/oauth2/client'
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
// ========== 列表相关 ==========
|
||||
const { register, tableObject, methods } = useTable<PageResult<OAuth2ClientVo>, OAuth2ClientVo>({
|
||||
getListApi: ClientApi.getOAuth2ClientPageApi,
|
||||
delListApi: ClientApi.deleteOAuth2ClientApi
|
||||
})
|
||||
const { getList, setSearchParams, delList } = methods
|
||||
|
||||
// ========== CRUD 相关 ==========
|
||||
const actionLoading = ref(false) // 遮罩层
|
||||
const actionType = ref('') // 操作按钮的类型
|
||||
const dialogVisible = ref(false) // 是否显示弹出层
|
||||
const dialogTitle = ref('edit') // 弹出层标题
|
||||
const formRef = ref<FormExpose>() // 表单 Ref
|
||||
|
||||
// 设置标题
|
||||
const setDialogTile = (type: string) => {
|
||||
dialogTitle.value = t('action.' + type)
|
||||
actionType.value = type
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
// 新增操作
|
||||
const handleCreate = () => {
|
||||
setDialogTile('create')
|
||||
// 重置表单
|
||||
unref(formRef)?.getElFormRef()?.resetFields()
|
||||
}
|
||||
|
||||
// 修改操作
|
||||
const handleUpdate = async (row: OAuth2ClientVo) => {
|
||||
setDialogTile('update')
|
||||
// 设置数据
|
||||
const res = await ClientApi.getOAuth2ClientApi(row.id)
|
||||
unref(formRef)?.setValues(res)
|
||||
}
|
||||
|
||||
// 提交按钮
|
||||
const submitForm = async () => {
|
||||
actionLoading.value = true
|
||||
// 提交请求
|
||||
try {
|
||||
const data = unref(formRef)?.formModel as OAuth2ClientVo
|
||||
if (actionType.value === 'create') {
|
||||
await ClientApi.createOAuth2ClientApi(data)
|
||||
ElMessage.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await ClientApi.updateOAuth2ClientApi(data)
|
||||
ElMessage.success(t('common.updateSuccess'))
|
||||
}
|
||||
// 操作成功,重新加载列表
|
||||
dialogVisible.value = false
|
||||
await getList()
|
||||
} finally {
|
||||
actionLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 删除操作
|
||||
const handleDelete = (row: OAuth2ClientVo) => {
|
||||
delList(row.id, false)
|
||||
}
|
||||
|
||||
// ========== 详情相关 ==========
|
||||
const detailRef = ref() // 详情 Ref
|
||||
|
||||
// 详情操作
|
||||
const handleDetail = async (row: OAuth2ClientVo) => {
|
||||
// 设置数据
|
||||
detailRef.value = row
|
||||
setDialogTile('detail')
|
||||
}
|
||||
|
||||
// ========== 初始化 ==========
|
||||
getList()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 搜索工作区 -->
|
||||
<ContentWrap>
|
||||
<Search :schema="allSchemas.searchSchema" @search="setSearchParams" @reset="setSearchParams" />
|
||||
</ContentWrap>
|
||||
<ContentWrap>
|
||||
<!-- 操作工具栏 -->
|
||||
<div class="mb-10px">
|
||||
<el-button type="primary" v-hasPermi="['system:oauth2-client:create']" @click="handleCreate">
|
||||
<Icon icon="el:zoom-in" class="mr-5px" /> {{ t('action.add') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<!-- 列表 -->
|
||||
<Table
|
||||
:columns="allSchemas.tableColumns"
|
||||
:selection="false"
|
||||
:data="tableObject.tableList"
|
||||
:loading="tableObject.loading"
|
||||
:pagination="{
|
||||
total: tableObject.total
|
||||
}"
|
||||
v-model:pageSize="tableObject.pageSize"
|
||||
v-model:currentPage="tableObject.currentPage"
|
||||
@register="register"
|
||||
>
|
||||
<template #logo="{ row }">
|
||||
<el-image :src="row.logo" />
|
||||
</template>
|
||||
<template #status="{ row }">
|
||||
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
|
||||
</template>
|
||||
<template #createTime="{ row }">
|
||||
<span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:oauth2-client:update']"
|
||||
@click="handleUpdate(row)"
|
||||
>
|
||||
<Icon icon="ep:edit" class="mr-5px" /> {{ t('action.edit') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:oauth2-client:update']"
|
||||
@click="handleDetail(row)"
|
||||
>
|
||||
<Icon icon="ep:view" class="mr-5px" /> {{ t('action.detail') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:oauth2-client:delete']"
|
||||
@click="handleDelete(row)"
|
||||
>
|
||||
<Icon icon="ep:delete" class="mr-5px" /> {{ t('action.del') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</Table>
|
||||
</ContentWrap>
|
||||
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle">
|
||||
<!-- 对话框(添加 / 修改) -->
|
||||
<Form
|
||||
v-if="['create', 'update'].includes(actionType)"
|
||||
:schema="allSchemas.formSchema"
|
||||
:rules="rules"
|
||||
ref="formRef"
|
||||
/>
|
||||
<!-- 对话框(详情) -->
|
||||
<Descriptions
|
||||
v-if="actionType === 'detail'"
|
||||
:schema="allSchemas.detailSchema"
|
||||
:data="detailRef"
|
||||
>
|
||||
<template #status="{ row }">
|
||||
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
|
||||
</template>
|
||||
<template #createTime="{ row }">
|
||||
<span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
</template>
|
||||
</Descriptions>
|
||||
<!-- 操作按钮 -->
|
||||
<template #footer>
|
||||
<el-button
|
||||
v-if="['create', 'update'].includes(actionType)"
|
||||
type="primary"
|
||||
:loading="actionLoading"
|
||||
@click="submitForm"
|
||||
>
|
||||
{{ t('action.save') }}
|
||||
</el-button>
|
||||
<el-button @click="dialogVisible = false">{{ t('dialog.close') }}</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
92
yudao-ui-admin-vue3/src/views/system/oauth2/token/index.vue
Normal file
92
yudao-ui-admin-vue3/src/views/system/oauth2/token/index.vue
Normal file
@@ -0,0 +1,92 @@
|
||||
<script setup lang="ts">
|
||||
import dayjs from 'dayjs'
|
||||
import { useTable } from '@/hooks/web/useTable'
|
||||
import { allSchemas } from './token.data'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import type { OAuth2TokenVo } from '@/api/system/oauth2/token.types'
|
||||
import * as TokenApi from '@/api/system/oauth2/token'
|
||||
import { ref } from 'vue'
|
||||
const { t } = useI18n() // 国际化
|
||||
// ========== 列表相关 ==========
|
||||
const { register, tableObject, methods } = useTable<PageResult<OAuth2TokenVo>, OAuth2TokenVo>({
|
||||
getListApi: TokenApi.getAccessTokenPageApi,
|
||||
delListApi: TokenApi.deleteAccessTokenApi
|
||||
})
|
||||
// ========== 详情相关 ==========
|
||||
const detailRef = ref() // 详情 Ref
|
||||
const dialogVisible = ref(false) // 是否显示弹出层
|
||||
const dialogTitle = ref(t('action.detail')) // 弹出层标题
|
||||
const { getList, setSearchParams, delList } = methods
|
||||
// 详情
|
||||
const handleDetail = (row: OAuth2TokenVo) => {
|
||||
// 设置数据
|
||||
detailRef.value = row
|
||||
dialogVisible.value = true
|
||||
}
|
||||
// 强退操作
|
||||
const handleForceLogout = (row: OAuth2TokenVo) => {
|
||||
delList(row.id, false)
|
||||
}
|
||||
getList()
|
||||
</script>
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<Search :schema="allSchemas.searchSchema" @search="setSearchParams" @reset="setSearchParams" />
|
||||
</ContentWrap>
|
||||
<ContentWrap>
|
||||
<Table
|
||||
:columns="allSchemas.tableColumns"
|
||||
:selection="false"
|
||||
:data="tableObject.tableList"
|
||||
:loading="tableObject.loading"
|
||||
:pagination="{
|
||||
total: tableObject.total
|
||||
}"
|
||||
v-model:pageSize="tableObject.pageSize"
|
||||
v-model:currentPage="tableObject.currentPage"
|
||||
@register="register"
|
||||
>
|
||||
<template #userType="{ row }">
|
||||
<DictTag :type="DICT_TYPE.USER_TYPE" :value="row.userType" />
|
||||
</template>
|
||||
<template #createTime="{ row }">
|
||||
<span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
</template>
|
||||
<template #expiresTime="{ row }">
|
||||
<span>{{ dayjs(row.expiresTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<el-button link type="primary" @click="handleDetail(row)">
|
||||
<Icon icon="ep:view" class="mr-5px" /> {{ t('action.detail') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:oauth2-token:delete']"
|
||||
@click="handleForceLogout(row)"
|
||||
>
|
||||
<Icon icon="ep:delete" class="mr-5px" /> {{ t('action.logout') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</Table>
|
||||
</ContentWrap>
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle">
|
||||
<!-- 对话框(详情) -->
|
||||
<Descriptions :schema="allSchemas.detailSchema" :data="detailRef">
|
||||
<template #userType="{ row }">
|
||||
<DictTag :type="DICT_TYPE.USER_TYPE" :value="row.userType" />
|
||||
</template>
|
||||
<template #createTime="{ row }">
|
||||
<span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
</template>
|
||||
<template #expiresTime="{ row }">
|
||||
<span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
</template>
|
||||
</Descriptions>
|
||||
<!-- 操作按钮 -->
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">{{ t('dialog.close') }}</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
@@ -0,0 +1,68 @@
|
||||
import { reactive } from 'vue'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { CrudSchema, useCrudSchemas } from '@/hooks/web/useCrudSchemas'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
const { t } = useI18n() // 国际化
|
||||
// CrudSchema
|
||||
const crudSchemas = reactive<CrudSchema[]>([
|
||||
{
|
||||
label: t('common.index'),
|
||||
field: 'id',
|
||||
type: 'index',
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '用户编号',
|
||||
field: 'userId',
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '访问令牌',
|
||||
field: 'accessToken'
|
||||
},
|
||||
{
|
||||
label: '刷新令牌',
|
||||
field: 'refreshToken'
|
||||
},
|
||||
{
|
||||
label: '用户类型',
|
||||
field: 'userType',
|
||||
dictType: DICT_TYPE.USER_TYPE,
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('common.createTime'),
|
||||
field: 'createTime',
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '过期时间',
|
||||
field: 'expiresTime',
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('table.action'),
|
||||
field: 'action',
|
||||
width: '200px',
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
])
|
||||
export const { allSchemas } = useCrudSchemas(crudSchemas)
|
101
yudao-ui-admin-vue3/src/views/system/operatelog/index.vue
Normal file
101
yudao-ui-admin-vue3/src/views/system/operatelog/index.vue
Normal file
@@ -0,0 +1,101 @@
|
||||
<script setup lang="ts">
|
||||
import dayjs from 'dayjs'
|
||||
import { useTable } from '@/hooks/web/useTable'
|
||||
import { allSchemas } from './operateLog.data'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import type { OperateLogVO } from '@/api/system/operateLog/types'
|
||||
import * as OperateLogApi from '@/api/system/operateLog'
|
||||
import { ref } from 'vue'
|
||||
const { t } = useI18n() // 国际化
|
||||
// ========== 列表相关 ==========
|
||||
const { register, tableObject, methods } = useTable<PageResult<OperateLogVO>, OperateLogVO>({
|
||||
getListApi: OperateLogApi.getOperateLogPageApi,
|
||||
exportListApi: OperateLogApi.exportOperateLogApi
|
||||
})
|
||||
// ========== 详情相关 ==========
|
||||
const detailRef = ref() // 详情 Ref
|
||||
const dialogVisible = ref(false) // 是否显示弹出层
|
||||
const dialogTitle = ref(t('action.detail')) // 弹出层标题
|
||||
const { getList, setSearchParams, exportList } = methods
|
||||
// 导出操作
|
||||
const handleExport = async () => {
|
||||
await exportList('数据.xls')
|
||||
}
|
||||
// 详情
|
||||
const handleDetail = (row: OperateLogVO) => {
|
||||
// 设置数据
|
||||
detailRef.value = row
|
||||
dialogVisible.value = true
|
||||
}
|
||||
getList()
|
||||
</script>
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<Search :schema="allSchemas.searchSchema" @search="setSearchParams" @reset="setSearchParams" />
|
||||
</ContentWrap>
|
||||
<ContentWrap>
|
||||
<!-- 操作工具栏 -->
|
||||
<div class="mb-10px">
|
||||
<el-button
|
||||
type="warning"
|
||||
v-hasPermi="['system:operate-log:export']"
|
||||
:loading="tableObject.exportLoading"
|
||||
@click="handleExport"
|
||||
>
|
||||
<Icon icon="ep:download" class="mr-5px" /> {{ t('action.export') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<Table
|
||||
:columns="allSchemas.tableColumns"
|
||||
:selection="false"
|
||||
:data="tableObject.tableList"
|
||||
:loading="tableObject.loading"
|
||||
:pagination="{
|
||||
total: tableObject.total
|
||||
}"
|
||||
v-model:pageSize="tableObject.pageSize"
|
||||
v-model:currentPage="tableObject.currentPage"
|
||||
@register="register"
|
||||
>
|
||||
<template #type="{ row }">
|
||||
<DictTag :type="DICT_TYPE.SYSTEM_OPERATE_TYPE" :value="row.type" />
|
||||
</template>
|
||||
<template #duration="{ row }">
|
||||
<span>{{ row.duration + 'ms' }}</span>
|
||||
</template>
|
||||
<template #resultCode="{ row }">
|
||||
<span>{{ row.resultCode === 0 ? '成功' : '失败' }}</span>
|
||||
</template>
|
||||
<template #startTime="{ row }">
|
||||
<span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<el-button link type="primary" @click="handleDetail(row)">
|
||||
<Icon icon="ep:view" class="mr-5px" /> {{ t('action.detail') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</Table>
|
||||
</ContentWrap>
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle">
|
||||
<!-- 对话框(详情) -->
|
||||
<Descriptions :schema="allSchemas.detailSchema" :data="detailRef">
|
||||
<template #resultCode="{ row }">
|
||||
<span>{{ row.resultCode === 0 ? '成功' : '失败' }}</span>
|
||||
</template>
|
||||
<template #type="{ row }">
|
||||
<DictTag :type="DICT_TYPE.SYSTEM_OPERATE_TYPE" :value="row.type" />
|
||||
</template>
|
||||
<template #duration="{ row }">
|
||||
<span>{{ row.duration + 'ms' }}</span>
|
||||
</template>
|
||||
<template #startTime="{ row }">
|
||||
<span>{{ dayjs(row.startTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
</template>
|
||||
</Descriptions>
|
||||
<!-- 操作按钮 -->
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">{{ t('dialog.close') }}</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
@@ -0,0 +1,112 @@
|
||||
import { reactive } from 'vue'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { CrudSchema, useCrudSchemas } from '@/hooks/web/useCrudSchemas'
|
||||
const { t } = useI18n() // 国际化
|
||||
const crudSchemas = reactive<CrudSchema[]>([
|
||||
{
|
||||
label: t('common.index'),
|
||||
field: 'id',
|
||||
type: 'index',
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '操作模块',
|
||||
field: 'module',
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '操作名',
|
||||
field: 'name'
|
||||
},
|
||||
{
|
||||
label: '操作类型',
|
||||
field: 'type',
|
||||
dictType: DICT_TYPE.SYSTEM_OPERATE_TYPE,
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '请求方法名',
|
||||
field: 'requestMethod'
|
||||
},
|
||||
{
|
||||
label: '请求地址',
|
||||
field: 'requestUrl'
|
||||
},
|
||||
{
|
||||
label: '操作人员',
|
||||
field: 'userNickname'
|
||||
},
|
||||
{
|
||||
label: '操作明细',
|
||||
field: 'content',
|
||||
table: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '用户 IP',
|
||||
field: 'userIp',
|
||||
table: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'userAgent',
|
||||
field: 'userAgent'
|
||||
},
|
||||
{
|
||||
label: '操作结果',
|
||||
field: 'resultCode'
|
||||
},
|
||||
{
|
||||
label: '操作日期',
|
||||
field: 'startTime',
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '执行时长',
|
||||
field: 'duration'
|
||||
},
|
||||
{
|
||||
label: '操作日期',
|
||||
field: 'daterange',
|
||||
table: {
|
||||
show: false
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
},
|
||||
search: {
|
||||
show: true,
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
type: 'daterange',
|
||||
valueFormat: 'YYYY-MM-DD'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('table.action'),
|
||||
field: 'action',
|
||||
width: '80px',
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
])
|
||||
export const { allSchemas } = useCrudSchemas(crudSchemas)
|
198
yudao-ui-admin-vue3/src/views/system/post/index.vue
Normal file
198
yudao-ui-admin-vue3/src/views/system/post/index.vue
Normal file
@@ -0,0 +1,198 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, unref } from 'vue'
|
||||
import dayjs from 'dayjs'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { useTable } from '@/hooks/web/useTable'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { FormExpose } from '@/components/Form'
|
||||
import type { PostVO } from '@/api/system/post/types'
|
||||
import { rules, allSchemas } from './post.data'
|
||||
import * as PostApi from '@/api/system/post'
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
// ========== 列表相关 ==========
|
||||
const { register, tableObject, methods } = useTable<PageResult<PostVO>, PostVO>({
|
||||
getListApi: PostApi.getPostPageApi,
|
||||
delListApi: PostApi.deletePostApi,
|
||||
exportListApi: PostApi.exportPostApi
|
||||
})
|
||||
const { getList, setSearchParams, delList, exportList } = methods
|
||||
|
||||
// 导出操作
|
||||
const handleExport = async () => {
|
||||
await exportList('用户数据.xls')
|
||||
}
|
||||
|
||||
// ========== CRUD 相关 ==========
|
||||
const actionLoading = ref(false) // 遮罩层
|
||||
const actionType = ref('') // 操作按钮的类型
|
||||
const dialogVisible = ref(false) // 是否显示弹出层
|
||||
const dialogTitle = ref('edit') // 弹出层标题
|
||||
const formRef = ref<FormExpose>() // 表单 Ref
|
||||
|
||||
// 设置标题
|
||||
const setDialogTile = (type: string) => {
|
||||
dialogTitle.value = t('action.' + type)
|
||||
actionType.value = type
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
// 新增操作
|
||||
const handleCreate = () => {
|
||||
setDialogTile('create')
|
||||
// 重置表单
|
||||
unref(formRef)?.getElFormRef()?.resetFields()
|
||||
}
|
||||
|
||||
// 修改操作
|
||||
const handleUpdate = async (row: PostVO) => {
|
||||
setDialogTile('update')
|
||||
// 设置数据
|
||||
const res = await PostApi.getPostApi(row.id)
|
||||
unref(formRef)?.setValues(res)
|
||||
}
|
||||
|
||||
// 提交按钮
|
||||
const submitForm = async () => {
|
||||
actionLoading.value = true
|
||||
// 提交请求
|
||||
try {
|
||||
const data = unref(formRef)?.formModel as PostVO
|
||||
if (actionType.value === 'create') {
|
||||
await PostApi.createPostApi(data)
|
||||
ElMessage.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await PostApi.updatePostApi(data)
|
||||
ElMessage.success(t('common.updateSuccess'))
|
||||
}
|
||||
// 操作成功,重新加载列表
|
||||
dialogVisible.value = false
|
||||
await getList()
|
||||
} finally {
|
||||
actionLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 删除操作
|
||||
const handleDelete = (row: PostVO) => {
|
||||
delList(row.id, false)
|
||||
}
|
||||
|
||||
// ========== 详情相关 ==========
|
||||
const detailRef = ref() // 详情 Ref
|
||||
|
||||
// 详情操作
|
||||
const handleDetail = async (row: PostVO) => {
|
||||
// 设置数据
|
||||
detailRef.value = row
|
||||
setDialogTile('detail')
|
||||
}
|
||||
|
||||
// ========== 初始化 ==========
|
||||
getList()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 搜索工作区 -->
|
||||
<ContentWrap>
|
||||
<Search :schema="allSchemas.searchSchema" @search="setSearchParams" @reset="setSearchParams" />
|
||||
</ContentWrap>
|
||||
<ContentWrap>
|
||||
<!-- 操作工具栏 -->
|
||||
<div class="mb-10px">
|
||||
<el-button type="primary" v-hasPermi="['system:post:create']" @click="handleCreate">
|
||||
<Icon icon="el:zoom-in" class="mr-5px" /> {{ t('action.add') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
v-hasPermi="['system:post:export']"
|
||||
:loading="tableObject.exportLoading"
|
||||
@click="handleExport"
|
||||
>
|
||||
<Icon icon="ep:download" class="mr-5px" /> {{ t('action.export') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<!-- 列表 -->
|
||||
<Table
|
||||
:columns="allSchemas.tableColumns"
|
||||
:selection="false"
|
||||
:data="tableObject.tableList"
|
||||
:loading="tableObject.loading"
|
||||
:pagination="{
|
||||
total: tableObject.total
|
||||
}"
|
||||
v-model:pageSize="tableObject.pageSize"
|
||||
v-model:currentPage="tableObject.currentPage"
|
||||
@register="register"
|
||||
>
|
||||
<template #status="{ row }">
|
||||
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
|
||||
</template>
|
||||
<template #createTime="{ row }">
|
||||
<span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:post:update']"
|
||||
@click="handleUpdate(row)"
|
||||
>
|
||||
<Icon icon="ep:edit" class="mr-5px" /> {{ t('action.edit') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:post:update']"
|
||||
@click="handleDetail(row)"
|
||||
>
|
||||
<Icon icon="ep:view" class="mr-5px" /> {{ t('action.detail') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:post:delete']"
|
||||
@click="handleDelete(row)"
|
||||
>
|
||||
<Icon icon="ep:delete" class="mr-5px" /> {{ t('action.del') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</Table>
|
||||
</ContentWrap>
|
||||
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle">
|
||||
<!-- 对话框(添加 / 修改) -->
|
||||
<Form
|
||||
v-if="['create', 'update'].includes(actionType)"
|
||||
:schema="allSchemas.formSchema"
|
||||
:rules="rules"
|
||||
ref="formRef"
|
||||
/>
|
||||
<!-- 对话框(详情) -->
|
||||
<Descriptions
|
||||
v-if="actionType === 'detail'"
|
||||
:schema="allSchemas.detailSchema"
|
||||
:data="detailRef"
|
||||
>
|
||||
<template #status="{ row }">
|
||||
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
|
||||
</template>
|
||||
<template #createTime="{ row }">
|
||||
<span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
</template>
|
||||
</Descriptions>
|
||||
<!-- 操作按钮 -->
|
||||
<template #footer>
|
||||
<el-button
|
||||
v-if="['create', 'update'].includes(actionType)"
|
||||
type="primary"
|
||||
:loading="actionLoading"
|
||||
@click="submitForm"
|
||||
>
|
||||
{{ t('action.save') }}
|
||||
</el-button>
|
||||
<el-button @click="dialogVisible = false">{{ t('dialog.close') }}</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
74
yudao-ui-admin-vue3/src/views/system/post/post.data.ts
Normal file
74
yudao-ui-admin-vue3/src/views/system/post/post.data.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { reactive } from 'vue'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { required } from '@/utils/formRules'
|
||||
import { CrudSchema, useCrudSchemas } from '@/hooks/web/useCrudSchemas'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
// 表单校验
|
||||
export const rules = reactive({
|
||||
name: [required],
|
||||
code: [required],
|
||||
sort: [required],
|
||||
status: [required]
|
||||
})
|
||||
|
||||
// CrudSchema
|
||||
const crudSchemas = reactive<CrudSchema[]>([
|
||||
{
|
||||
label: t('common.index'),
|
||||
field: 'id',
|
||||
type: 'index',
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '岗位名称',
|
||||
field: 'name',
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '岗位编码',
|
||||
field: 'code',
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '岗位顺序',
|
||||
field: 'sort'
|
||||
},
|
||||
{
|
||||
label: t('common.status'),
|
||||
field: 'status',
|
||||
dictType: DICT_TYPE.COMMON_STATUS,
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('common.createTime'),
|
||||
field: 'createTime',
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('table.action'),
|
||||
field: 'action',
|
||||
width: '240px',
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
])
|
||||
export const { allSchemas } = useCrudSchemas(crudSchemas)
|
327
yudao-ui-admin-vue3/src/views/system/role/index.vue
Normal file
327
yudao-ui-admin-vue3/src/views/system/role/index.vue
Normal file
@@ -0,0 +1,327 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref, unref } from 'vue'
|
||||
import dayjs from 'dayjs'
|
||||
import { DICT_TYPE, getDictOptions } from '@/utils/dict'
|
||||
import { useTable } from '@/hooks/web/useTable'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { FormExpose } from '@/components/Form'
|
||||
import type { RoleVO } from '@/api/system/role/types'
|
||||
import { rules, allSchemas } from './role.data'
|
||||
import * as RoleApi from '@/api/system/role'
|
||||
import Dialog from '@/components/Dialog/src/Dialog.vue'
|
||||
import {
|
||||
ElForm,
|
||||
ElFormItem,
|
||||
ElInput,
|
||||
ElSelect,
|
||||
ElOption,
|
||||
ElMessage,
|
||||
ElTree,
|
||||
ElCard,
|
||||
ElCheckbox
|
||||
} from 'element-plus'
|
||||
import { listSimpleMenusApi } from '@/api/system/menu'
|
||||
import { listSimpleDeptApi } from '@/api/system/dept'
|
||||
import { handleTree } from '@/utils/tree'
|
||||
import { SystemDataScopeEnum } from '@/utils/constants'
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
// ========== 列表相关 ==========
|
||||
const { register, tableObject, methods } = useTable<PageResult<RoleVO>, RoleVO>({
|
||||
getListApi: RoleApi.getRolePageApi,
|
||||
delListApi: RoleApi.deleteRoleApi
|
||||
})
|
||||
const { getList, setSearchParams, delList } = methods
|
||||
|
||||
// ========== CRUD 相关 ==========
|
||||
const loading = ref(false) // 遮罩层
|
||||
const actionType = ref('') // 操作按钮的类型
|
||||
const dialogVisible = ref(false) // 是否显示弹出层
|
||||
const dialogTitle = ref('edit') // 弹出层标题
|
||||
const formRef = ref<FormExpose>() // 表单 Ref
|
||||
|
||||
// 设置标题
|
||||
const setDialogTile = (type: string) => {
|
||||
dialogTitle.value = t('action.' + type)
|
||||
actionType.value = type
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
// 新增操作
|
||||
const handleCreate = () => {
|
||||
setDialogTile('create')
|
||||
// 重置表单
|
||||
unref(formRef)?.getElFormRef()?.resetFields()
|
||||
}
|
||||
|
||||
// 修改操作
|
||||
const handleUpdate = async (row: RoleVO) => {
|
||||
setDialogTile('update')
|
||||
// 设置数据
|
||||
const res = await RoleApi.getRoleApi(row.id)
|
||||
unref(formRef)?.setValues(res)
|
||||
}
|
||||
|
||||
// 提交按钮
|
||||
const submitForm = async () => {
|
||||
loading.value = true
|
||||
// 提交请求
|
||||
try {
|
||||
const data = unref(formRef)?.formModel as RoleVO
|
||||
if (actionType.value === 'create') {
|
||||
await RoleApi.createRoleApi(data)
|
||||
ElMessage.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await RoleApi.updateRoleApi(data)
|
||||
ElMessage.success(t('common.updateSuccess'))
|
||||
}
|
||||
// 操作成功,重新加载列表
|
||||
dialogVisible.value = false
|
||||
await getList()
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 删除操作
|
||||
const handleDelete = (row: RoleVO) => {
|
||||
delList(row.id, false)
|
||||
}
|
||||
|
||||
// ========== 详情相关 ==========
|
||||
const detailRef = ref() // 详情 Ref
|
||||
|
||||
// 详情操作
|
||||
const handleDetail = async (row: RoleVO) => {
|
||||
// 设置数据
|
||||
detailRef.value = row
|
||||
setDialogTile('detail')
|
||||
}
|
||||
|
||||
// ========== 数据权限 ==========
|
||||
const dataScopeForm = reactive({
|
||||
name: '',
|
||||
code: '',
|
||||
dataScope: 0,
|
||||
checkStrictly: true,
|
||||
checkList: []
|
||||
})
|
||||
const defaultProps = {
|
||||
children: 'children',
|
||||
label: 'name',
|
||||
value: 'id'
|
||||
}
|
||||
const treeOptions = ref([]) // 菜单树形结构
|
||||
const treeRef = ref<InstanceType<typeof ElTree>>()
|
||||
const dialogScopeVisible = ref(false)
|
||||
const dialogScopeTitle = ref('数据权限')
|
||||
const actionScopeType = ref('')
|
||||
const dataScopeDictDatas = ref()
|
||||
// 选项
|
||||
const treeNodeAll = ref(false)
|
||||
// 权限操作
|
||||
const handleScope = async (type: string, row: RoleVO) => {
|
||||
dataScopeForm.name = row.name
|
||||
dataScopeForm.code = row.code
|
||||
if (type === 'menu') {
|
||||
const menuRes = await listSimpleMenusApi()
|
||||
treeOptions.value = handleTree(menuRes)
|
||||
dataScopeDictDatas.value = getDictOptions(DICT_TYPE.SYSTEM_DATA_SCOPE)
|
||||
} else if (type === 'dept') {
|
||||
const deptRes = await listSimpleDeptApi()
|
||||
treeOptions.value = handleTree(deptRes)
|
||||
}
|
||||
actionScopeType.value = type
|
||||
dialogScopeVisible.value = true
|
||||
}
|
||||
// 树权限(父子联动)
|
||||
const handleCheckedTreeConnect = (value) => {
|
||||
dataScopeForm.checkStrictly = value ? true : false
|
||||
}
|
||||
// 全选/全不选
|
||||
const handleCheckedTreeNodeAll = (value) => {
|
||||
treeRef.value?.setCheckedNodes(value ? dataScopeForm.checkList : [])
|
||||
}
|
||||
// TODO:保存
|
||||
const submitScope = () => {
|
||||
console.info()
|
||||
}
|
||||
// ========== 初始化 ==========
|
||||
getList()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 搜索工作区 -->
|
||||
<ContentWrap>
|
||||
<Search :schema="allSchemas.searchSchema" @search="setSearchParams" @reset="setSearchParams" />
|
||||
</ContentWrap>
|
||||
<ContentWrap>
|
||||
<!-- 操作工具栏 -->
|
||||
<div class="mb-10px">
|
||||
<el-button type="primary" v-hasPermi="['system:role:create']" @click="handleCreate">
|
||||
<Icon icon="el:zoom-in" class="mr-5px" /> {{ t('action.add') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<!-- 列表 -->
|
||||
<Table
|
||||
:columns="allSchemas.tableColumns"
|
||||
:selection="false"
|
||||
:data="tableObject.tableList"
|
||||
:loading="tableObject.loading"
|
||||
:pagination="{
|
||||
total: tableObject.total
|
||||
}"
|
||||
v-model:pageSize="tableObject.pageSize"
|
||||
v-model:currentPage="tableObject.currentPage"
|
||||
@register="register"
|
||||
>
|
||||
<template #type="{ row }">
|
||||
<DictTag :type="DICT_TYPE.SYSTEM_ROLE_TYPE" :value="row.type" />
|
||||
</template>
|
||||
<template #status="{ row }">
|
||||
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
|
||||
</template>
|
||||
<template #createTime="{ row }">
|
||||
<span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:role:update']"
|
||||
@click="handleUpdate(row)"
|
||||
>
|
||||
<Icon icon="ep:edit" class="mr-5px" /> {{ t('action.edit') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:role:update']"
|
||||
@click="handleDetail(row)"
|
||||
>
|
||||
<Icon icon="ep:view" class="mr-5px" /> {{ t('action.detail') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:permission:assign-role-menu']"
|
||||
@click="handleScope('menu', row)"
|
||||
>
|
||||
<Icon icon="ep:basketball" class="mr-5px" /> 菜单权限
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:permission:assign-role-data-scope']"
|
||||
@click="handleScope('data', row)"
|
||||
>
|
||||
<Icon icon="ep:coin" class="mr-5px" /> 数据权限
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:role:delete']"
|
||||
@click="handleDelete(row)"
|
||||
>
|
||||
<Icon icon="ep:delete" class="mr-5px" /> {{ t('action.del') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</Table>
|
||||
</ContentWrap>
|
||||
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle">
|
||||
<!-- 对话框(添加 / 修改) -->
|
||||
<Form
|
||||
v-if="['create', 'update'].includes(actionType)"
|
||||
:schema="allSchemas.formSchema"
|
||||
:rules="rules"
|
||||
ref="formRef"
|
||||
/>
|
||||
<!-- 对话框(详情) -->
|
||||
<Descriptions
|
||||
v-if="actionType === 'detail'"
|
||||
:schema="allSchemas.detailSchema"
|
||||
:data="detailRef"
|
||||
>
|
||||
<template #type="{ row }">
|
||||
<DictTag :type="DICT_TYPE.SYSTEM_ROLE_TYPE" :value="row.type" />
|
||||
</template>
|
||||
<template #status="{ row }">
|
||||
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
|
||||
</template>
|
||||
<template #createTime="{ row }">
|
||||
<span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
</template>
|
||||
</Descriptions>
|
||||
<!-- 操作按钮 -->
|
||||
<template #footer>
|
||||
<el-button
|
||||
v-if="['create', 'update'].includes(actionType)"
|
||||
type="primary"
|
||||
:loading="loading"
|
||||
@click="submitForm"
|
||||
>
|
||||
{{ t('action.save') }}
|
||||
</el-button>
|
||||
<el-button @click="dialogVisible = false">{{ t('dialog.close') }}</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
<Dialog v-model="dialogScopeVisible" :title="dialogScopeTitle">
|
||||
<el-form :model="dataScopeForm">
|
||||
<el-form-item label="角色名称">
|
||||
<el-input v-model="dataScopeForm.name" :disabled="true" />
|
||||
</el-form-item>
|
||||
<el-form-item label="角色标识">
|
||||
<el-input v-model="dataScopeForm.code" :disabled="true" />
|
||||
</el-form-item>
|
||||
<!-- 分配角色的数据权限对话框 -->
|
||||
<el-form-item label="权限范围" v-if="actionScopeType === 'data'">
|
||||
<el-select v-model="dataScopeForm.dataScope">
|
||||
<el-option
|
||||
v-for="item in dataScopeDictDatas"
|
||||
:key="parseInt(item.value)"
|
||||
:label="item.label"
|
||||
:value="parseInt(item.value)"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- 分配角色的菜单权限对话框 -->
|
||||
<el-form-item
|
||||
label="权限范围"
|
||||
v-if="
|
||||
actionScopeType === 'menu' || dataScopeForm.dataScope === SystemDataScopeEnum.DEPT_CUSTOM
|
||||
"
|
||||
>
|
||||
<el-card class="box-card">
|
||||
<template #header>
|
||||
<el-checkbox
|
||||
:checked="!dataScopeForm.checkStrictly"
|
||||
@change="handleCheckedTreeConnect($event)"
|
||||
>父子联动(选中父节点,自动选择子节点)
|
||||
</el-checkbox>
|
||||
<el-checkbox v-model="treeNodeAll" @change="handleCheckedTreeNodeAll($event)">
|
||||
全选/全不选
|
||||
</el-checkbox>
|
||||
</template>
|
||||
<el-tree
|
||||
ref="treeRef"
|
||||
node-key="id"
|
||||
show-checkbox
|
||||
default-expand-all
|
||||
:check-strictly="dataScopeForm.checkStrictly"
|
||||
:props="defaultProps"
|
||||
:data="treeOptions"
|
||||
empty-text="加载中,请稍后"
|
||||
/>
|
||||
</el-card>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<!-- 操作按钮 -->
|
||||
<template #footer>
|
||||
<el-button type="primary" :loading="loading" @click="submitScope">
|
||||
{{ t('action.save') }}
|
||||
</el-button>
|
||||
<el-button @click="dialogScopeVisible = false">{{ t('dialog.close') }}</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
89
yudao-ui-admin-vue3/src/views/system/role/role.data.ts
Normal file
89
yudao-ui-admin-vue3/src/views/system/role/role.data.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import { reactive } from 'vue'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { required } from '@/utils/formRules'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { CrudSchema, useCrudSchemas } from '@/hooks/web/useCrudSchemas'
|
||||
// 国际化
|
||||
const { t } = useI18n()
|
||||
// 表单校验
|
||||
export const rules = reactive({
|
||||
name: [required],
|
||||
code: [required],
|
||||
sort: [required]
|
||||
})
|
||||
// CrudSchema
|
||||
const crudSchemas = reactive<CrudSchema[]>([
|
||||
{
|
||||
label: t('common.index'),
|
||||
field: 'id',
|
||||
type: 'index',
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '角色名称',
|
||||
field: 'name',
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '角色类型',
|
||||
field: 'type',
|
||||
dictType: DICT_TYPE.SYSTEM_ROLE_TYPE
|
||||
},
|
||||
{
|
||||
label: '角色标识',
|
||||
field: 'code',
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '显示顺序',
|
||||
field: 'sort',
|
||||
form: {
|
||||
component: 'InputNumber',
|
||||
value: 0
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('common.status'),
|
||||
field: 'status',
|
||||
dictType: DICT_TYPE.COMMON_STATUS,
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('common.createTime'),
|
||||
field: 'createTime',
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
search: {
|
||||
show: true,
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
type: 'daterange',
|
||||
valueFormat: 'YYYY-MM-DD'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
width: '450px',
|
||||
label: t('table.action'),
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
])
|
||||
export const { allSchemas } = useCrudSchemas(crudSchemas)
|
222
yudao-ui-admin-vue3/src/views/system/sensitiveWord/index.vue
Normal file
222
yudao-ui-admin-vue3/src/views/system/sensitiveWord/index.vue
Normal file
@@ -0,0 +1,222 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, unref } from 'vue'
|
||||
import dayjs from 'dayjs'
|
||||
import { ElMessage, ElTag, ElSelect, ElOption } from 'element-plus'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { useTable } from '@/hooks/web/useTable'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { FormExpose } from '@/components/Form'
|
||||
import type { SensitiveWordVO } from '@/api/system/sensitiveWord/types'
|
||||
import { rules, allSchemas } from './sensitiveWord.data'
|
||||
import * as SensitiveWordApi from '@/api/system/sensitiveWord'
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
// ========== 列表相关 ==========
|
||||
const { register, tableObject, methods } = useTable<PageResult<SensitiveWordVO>, SensitiveWordVO>({
|
||||
getListApi: SensitiveWordApi.getSensitiveWordPageApi,
|
||||
delListApi: SensitiveWordApi.deleteSensitiveWordApi,
|
||||
exportListApi: SensitiveWordApi.exportSensitiveWordApi
|
||||
})
|
||||
const { getList, setSearchParams, delList, exportList } = methods
|
||||
|
||||
// 导出操作
|
||||
const handleExport = async () => {
|
||||
await exportList('敏感词数据.xls')
|
||||
}
|
||||
// 获取标签
|
||||
const tagsOptions = ref()
|
||||
const getTags = async () => {
|
||||
const res = await SensitiveWordApi.getSensitiveWordTagsApi()
|
||||
tagsOptions.value = res
|
||||
}
|
||||
// ========== CRUD 相关 ==========
|
||||
const actionLoading = ref(false) // 遮罩层
|
||||
const actionType = ref('') // 操作按钮的类型
|
||||
const dialogVisible = ref(false) // 是否显示弹出层
|
||||
const dialogTitle = ref('edit') // 弹出层标题
|
||||
const formRef = ref<FormExpose>() // 表单 Ref
|
||||
const tags = ref()
|
||||
// 设置标题
|
||||
const setDialogTile = (type: string) => {
|
||||
dialogTitle.value = t('action.' + type)
|
||||
actionType.value = type
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
// 新增操作
|
||||
const handleCreate = () => {
|
||||
setDialogTile('create')
|
||||
// 重置表单
|
||||
unref(formRef)?.getElFormRef()?.resetFields()
|
||||
}
|
||||
|
||||
// 修改操作
|
||||
const handleUpdate = async (row: SensitiveWordVO) => {
|
||||
setDialogTile('update')
|
||||
// 设置数据
|
||||
const res = await SensitiveWordApi.getSensitiveWordApi(row.id)
|
||||
unref(formRef)?.setValues(res)
|
||||
}
|
||||
|
||||
// 提交按钮
|
||||
const submitForm = async () => {
|
||||
actionLoading.value = true
|
||||
// 提交请求
|
||||
try {
|
||||
const data = unref(formRef)?.formModel as SensitiveWordVO
|
||||
if (actionType.value === 'create') {
|
||||
await SensitiveWordApi.createSensitiveWordApi(data)
|
||||
ElMessage.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await SensitiveWordApi.updateSensitiveWordApi(data)
|
||||
ElMessage.success(t('common.updateSuccess'))
|
||||
}
|
||||
// 操作成功,重新加载列表
|
||||
dialogVisible.value = false
|
||||
await getList()
|
||||
} finally {
|
||||
actionLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 删除操作
|
||||
const handleDelete = (row: SensitiveWordVO) => {
|
||||
delList(row.id, false)
|
||||
}
|
||||
|
||||
// ========== 详情相关 ==========
|
||||
const detailRef = ref() // 详情 Ref
|
||||
|
||||
// 详情操作
|
||||
const handleDetail = async (row: SensitiveWordVO) => {
|
||||
// 设置数据
|
||||
detailRef.value = row
|
||||
setDialogTile('detail')
|
||||
}
|
||||
|
||||
// ========== 初始化 ==========
|
||||
onMounted(async () => {
|
||||
await getTags()
|
||||
await getList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 搜索工作区 -->
|
||||
<ContentWrap>
|
||||
<Search :schema="allSchemas.searchSchema" @search="setSearchParams" @reset="setSearchParams" />
|
||||
</ContentWrap>
|
||||
<ContentWrap>
|
||||
<!-- 操作工具栏 -->
|
||||
<div class="mb-10px">
|
||||
<el-button type="primary" v-hasPermi="['system:post:create']" @click="handleCreate">
|
||||
<Icon icon="el:zoom-in" class="mr-5px" /> {{ t('action.add') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
v-hasPermi="['system:post:export']"
|
||||
:loading="tableObject.exportLoading"
|
||||
@click="handleExport"
|
||||
>
|
||||
<Icon icon="ep:download" class="mr-5px" /> {{ t('action.export') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<!-- 列表 -->
|
||||
<Table
|
||||
:columns="allSchemas.tableColumns"
|
||||
:selection="false"
|
||||
:data="tableObject.tableList"
|
||||
:loading="tableObject.loading"
|
||||
:pagination="{
|
||||
total: tableObject.total
|
||||
}"
|
||||
v-model:pageSize="tableObject.pageSize"
|
||||
v-model:currentPage="tableObject.currentPage"
|
||||
@register="register"
|
||||
>
|
||||
<template #tags="{ row }">
|
||||
<el-tag
|
||||
:disable-transitions="true"
|
||||
:key="index"
|
||||
v-for="(tag, index) in row.tags"
|
||||
:index="index"
|
||||
>
|
||||
{{ tag }}
|
||||
</el-tag>
|
||||
</template>
|
||||
<template #status="{ row }">
|
||||
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
|
||||
</template>
|
||||
<template #createTime="{ row }">
|
||||
<span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:post:update']"
|
||||
@click="handleUpdate(row)"
|
||||
>
|
||||
<Icon icon="ep:edit" class="mr-5px" /> {{ t('action.edit') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:post:update']"
|
||||
@click="handleDetail(row)"
|
||||
>
|
||||
<Icon icon="ep:view" class="mr-5px" /> {{ t('action.detail') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:post:delete']"
|
||||
@click="handleDelete(row)"
|
||||
>
|
||||
<Icon icon="ep:delete" class="mr-5px" /> {{ t('action.del') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</Table>
|
||||
</ContentWrap>
|
||||
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle">
|
||||
<!-- 对话框(添加 / 修改) -->
|
||||
<Form
|
||||
v-if="['create', 'update'].includes(actionType)"
|
||||
:schema="allSchemas.formSchema"
|
||||
:rules="rules"
|
||||
ref="formRef"
|
||||
>
|
||||
<template #tags>
|
||||
<el-select v-model="tags" multiple placeholder="请选择">
|
||||
<el-option v-for="item in tagsOptions" :key="item" :label="item" :value="item" />
|
||||
</el-select>
|
||||
</template>
|
||||
</Form>
|
||||
<!-- 对话框(详情) -->
|
||||
<Descriptions
|
||||
v-if="actionType === 'detail'"
|
||||
:schema="allSchemas.detailSchema"
|
||||
:data="detailRef"
|
||||
>
|
||||
<template #status="{ row }">
|
||||
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
|
||||
</template>
|
||||
<template #createTime="{ row }">
|
||||
<span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
</template>
|
||||
</Descriptions>
|
||||
<!-- 操作按钮 -->
|
||||
<template #footer>
|
||||
<el-button
|
||||
v-if="['create', 'update'].includes(actionType)"
|
||||
type="primary"
|
||||
:loading="actionLoading"
|
||||
@click="submitForm"
|
||||
>
|
||||
{{ t('action.save') }}
|
||||
</el-button>
|
||||
<el-button @click="dialogVisible = false">{{ t('dialog.close') }}</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
@@ -0,0 +1,79 @@
|
||||
import { reactive } from 'vue'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { required } from '@/utils/formRules'
|
||||
import { CrudSchema, useCrudSchemas } from '@/hooks/web/useCrudSchemas'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
// 表单校验
|
||||
export const rules = reactive({
|
||||
name: [required],
|
||||
tags: [required]
|
||||
})
|
||||
|
||||
// CrudSchema
|
||||
const crudSchemas = reactive<CrudSchema[]>([
|
||||
{
|
||||
label: t('common.index'),
|
||||
field: 'id',
|
||||
type: 'index',
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '敏感词',
|
||||
field: 'name',
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '标签',
|
||||
field: 'tags'
|
||||
},
|
||||
{
|
||||
label: t('common.status'),
|
||||
field: 'status',
|
||||
dictType: DICT_TYPE.COMMON_STATUS,
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '描述',
|
||||
field: 'description',
|
||||
form: {
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
type: 'textarea',
|
||||
rows: 4
|
||||
},
|
||||
colProps: {
|
||||
span: 24
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('common.createTime'),
|
||||
field: 'createTime',
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('table.action'),
|
||||
field: 'action',
|
||||
width: '240px',
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
])
|
||||
export const { allSchemas } = useCrudSchemas(crudSchemas)
|
190
yudao-ui-admin-vue3/src/views/system/sms/smsChannel/index.vue
Normal file
190
yudao-ui-admin-vue3/src/views/system/sms/smsChannel/index.vue
Normal file
@@ -0,0 +1,190 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, unref } from 'vue'
|
||||
import dayjs from 'dayjs'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { useTable } from '@/hooks/web/useTable'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { FormExpose } from '@/components/Form'
|
||||
import type { SmsChannelVO } from '@/api/system/sms/smsChannel/types'
|
||||
import { rules, allSchemas } from './sms.channel.data'
|
||||
import * as SmsChannelApi from '@/api/system/sms/smsChannel'
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
// ========== 列表相关 ==========
|
||||
const { register, tableObject, methods } = useTable<PageResult<SmsChannelVO>, SmsChannelVO>({
|
||||
getListApi: SmsChannelApi.getSmsChannelPageApi,
|
||||
delListApi: SmsChannelApi.deleteSmsChannelApi
|
||||
})
|
||||
const { getList, setSearchParams, delList } = methods
|
||||
|
||||
// ========== CRUD 相关 ==========
|
||||
const loading = ref(false) // 遮罩层
|
||||
const actionType = ref('') // 操作按钮的类型
|
||||
const dialogVisible = ref(false) // 是否显示弹出层
|
||||
const dialogTitle = ref('edit') // 弹出层标题
|
||||
const formRef = ref<FormExpose>() // 表单 Ref
|
||||
|
||||
// 设置标题
|
||||
const setDialogTile = (type: string) => {
|
||||
dialogTitle.value = t('action.' + type)
|
||||
actionType.value = type
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
// 新增操作
|
||||
const handleCreate = () => {
|
||||
setDialogTile('create')
|
||||
// 重置表单
|
||||
unref(formRef)?.getElFormRef()?.resetFields()
|
||||
}
|
||||
|
||||
// 修改操作
|
||||
const handleUpdate = async (row: SmsChannelVO) => {
|
||||
setDialogTile('update')
|
||||
// 设置数据
|
||||
const res = await SmsChannelApi.getSmsChannelApi(row.id)
|
||||
unref(formRef)?.setValues(res)
|
||||
}
|
||||
|
||||
// 提交按钮
|
||||
const submitForm = async () => {
|
||||
loading.value = true
|
||||
// 提交请求
|
||||
try {
|
||||
const data = unref(formRef)?.formModel as SmsChannelVO
|
||||
if (actionType.value === 'create') {
|
||||
await SmsChannelApi.createSmsChannelApi(data)
|
||||
ElMessage.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await SmsChannelApi.updateSmsChannelApi(data)
|
||||
ElMessage.success(t('common.updateSuccess'))
|
||||
}
|
||||
// 操作成功,重新加载列表
|
||||
dialogVisible.value = false
|
||||
await getList()
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 删除操作
|
||||
const handleDelete = (row: SmsChannelVO) => {
|
||||
delList(row.id, false)
|
||||
}
|
||||
|
||||
// ========== 详情相关 ==========
|
||||
const detailRef = ref() // 详情 Ref
|
||||
|
||||
// 详情操作
|
||||
const handleDetail = async (row: SmsChannelVO) => {
|
||||
// 设置数据
|
||||
detailRef.value = row
|
||||
setDialogTile('detail')
|
||||
}
|
||||
|
||||
// ========== 初始化 ==========
|
||||
getList()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 搜索工作区 -->
|
||||
<ContentWrap>
|
||||
<Search :schema="allSchemas.searchSchema" @search="setSearchParams" @reset="setSearchParams" />
|
||||
</ContentWrap>
|
||||
<ContentWrap>
|
||||
<!-- 操作工具栏 -->
|
||||
<div class="mb-10px">
|
||||
<el-button type="primary" v-hasPermi="['system:sms-channel:create']" @click="handleCreate">
|
||||
<Icon icon="el:zoom-in" class="mr-5px" /> {{ t('action.add') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<!-- 列表 -->
|
||||
<Table
|
||||
:columns="allSchemas.tableColumns"
|
||||
:selection="false"
|
||||
:data="tableObject.tableList"
|
||||
:loading="tableObject.loading"
|
||||
:pagination="{
|
||||
total: tableObject.total
|
||||
}"
|
||||
v-model:pageSize="tableObject.pageSize"
|
||||
v-model:currentPage="tableObject.currentPage"
|
||||
@register="register"
|
||||
>
|
||||
<template #code="{ row }">
|
||||
<DictTag :type="DICT_TYPE.SYSTEM_SMS_CHANNEL_CODE" :value="row.code" />
|
||||
</template>
|
||||
<template #status="{ row }">
|
||||
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
|
||||
</template>
|
||||
<template #createTime="{ row }">
|
||||
<span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:sms-channel:update']"
|
||||
@click="handleUpdate(row)"
|
||||
>
|
||||
<Icon icon="ep:edit" class="mr-5px" /> {{ t('action.edit') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:sms-channel:update']"
|
||||
@click="handleDetail(row)"
|
||||
>
|
||||
<Icon icon="ep:view" class="mr-5px" /> {{ t('action.detail') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:sms-channel:delete']"
|
||||
@click="handleDelete(row)"
|
||||
>
|
||||
<Icon icon="ep:delete" class="mr-5px" /> {{ t('action.del') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</Table>
|
||||
</ContentWrap>
|
||||
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle">
|
||||
<!-- 对话框(添加 / 修改) -->
|
||||
<Form
|
||||
v-if="['create', 'update'].includes(actionType)"
|
||||
:schema="allSchemas.formSchema"
|
||||
:rules="rules"
|
||||
ref="formRef"
|
||||
/>
|
||||
<!-- 对话框(详情) -->
|
||||
<Descriptions
|
||||
v-if="actionType === 'detail'"
|
||||
:schema="allSchemas.detailSchema"
|
||||
:data="detailRef"
|
||||
>
|
||||
<template #code="{ row }">
|
||||
<DictTag :type="DICT_TYPE.SYSTEM_SMS_CHANNEL_CODE" :value="row.code" />
|
||||
</template>
|
||||
<template #status="{ row }">
|
||||
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
|
||||
</template>
|
||||
<template #createTime="{ row }">
|
||||
<span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
</template>
|
||||
</Descriptions>
|
||||
<!-- 操作按钮 -->
|
||||
<template #footer>
|
||||
<el-button
|
||||
v-if="['create', 'update'].includes(actionType)"
|
||||
type="primary"
|
||||
:loading="loading"
|
||||
@click="submitForm"
|
||||
>
|
||||
{{ t('action.save') }}
|
||||
</el-button>
|
||||
<el-button @click="dialogVisible = false">{{ t('dialog.close') }}</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
@@ -0,0 +1,104 @@
|
||||
import { reactive } from 'vue'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { required } from '@/utils/formRules'
|
||||
import { CrudSchema, useCrudSchemas } from '@/hooks/web/useCrudSchemas'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
// 表单校验
|
||||
export const rules = reactive({
|
||||
signature: [required],
|
||||
code: [required],
|
||||
apiKey: [required],
|
||||
status: [required]
|
||||
})
|
||||
|
||||
// CrudSchema
|
||||
const crudSchemas = reactive<CrudSchema[]>([
|
||||
{
|
||||
label: t('common.index'),
|
||||
field: 'id',
|
||||
type: 'index',
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '短信签名',
|
||||
field: 'signature',
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '渠道编码',
|
||||
field: 'code',
|
||||
dictType: DICT_TYPE.SYSTEM_SMS_CHANNEL_CODE,
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('common.status'),
|
||||
field: 'status',
|
||||
dictType: DICT_TYPE.COMMON_STATUS,
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '短信 API 的账号',
|
||||
field: 'apiKey'
|
||||
},
|
||||
{
|
||||
label: '短信 API 的密钥',
|
||||
field: 'apiSecret'
|
||||
},
|
||||
{
|
||||
label: '短信发送回调 URL',
|
||||
field: 'callbackUrl'
|
||||
},
|
||||
{
|
||||
label: t('common.createTime'),
|
||||
field: 'createTime',
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('common.createTime'),
|
||||
field: 'daterange',
|
||||
table: {
|
||||
show: false
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
},
|
||||
search: {
|
||||
show: true,
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
type: 'daterange',
|
||||
valueFormat: 'YYYY-MM-DD'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('table.action'),
|
||||
field: 'action',
|
||||
width: '240px',
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
])
|
||||
export const { allSchemas } = useCrudSchemas(crudSchemas)
|
99
yudao-ui-admin-vue3/src/views/system/sms/smsLog/index.vue
Normal file
99
yudao-ui-admin-vue3/src/views/system/sms/smsLog/index.vue
Normal file
@@ -0,0 +1,99 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import dayjs from 'dayjs'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { useTable } from '@/hooks/web/useTable'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import type { SmsLogVO } from '@/api/system/sms/smsLog/types'
|
||||
import { allSchemas } from './sms.log.data'
|
||||
import * as SmsLoglApi from '@/api/system/sms/smsLog'
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
// ========== 列表相关 ==========
|
||||
const { register, tableObject, methods } = useTable<PageResult<SmsLogVO>, SmsLogVO>({
|
||||
getListApi: SmsLoglApi.getSmsLogPageApi
|
||||
})
|
||||
const { getList, setSearchParams } = methods
|
||||
|
||||
// ========== CRUD 相关 ==========
|
||||
const actionType = ref('') // 操作按钮的类型
|
||||
const dialogVisible = ref(false) // 是否显示弹出层
|
||||
const dialogTitle = ref(t('action.detail')) // 弹出层标题
|
||||
// ========== 详情相关 ==========
|
||||
const detailRef = ref() // 详情 Ref
|
||||
const handleDetail = (row: SmsLogVO) => {
|
||||
// 设置数据
|
||||
detailRef.value = row
|
||||
dialogVisible.value = true
|
||||
}
|
||||
// ========== 初始化 ==========
|
||||
getList()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 搜索工作区 -->
|
||||
<ContentWrap>
|
||||
<Search :schema="allSchemas.searchSchema" @search="setSearchParams" @reset="setSearchParams" />
|
||||
</ContentWrap>
|
||||
<ContentWrap>
|
||||
<!-- 列表 -->
|
||||
<Table
|
||||
:columns="allSchemas.tableColumns"
|
||||
:selection="false"
|
||||
:data="tableObject.tableList"
|
||||
:loading="tableObject.loading"
|
||||
:pagination="{
|
||||
total: tableObject.total
|
||||
}"
|
||||
v-model:pageSize="tableObject.pageSize"
|
||||
v-model:currentPage="tableObject.currentPage"
|
||||
@register="register"
|
||||
>
|
||||
<template #code="{ row }">
|
||||
<DictTag :type="DICT_TYPE.SYSTEM_SMS_CHANNEL_CODE" :value="row.code" />
|
||||
</template>
|
||||
<template #status="{ row }">
|
||||
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
|
||||
</template>
|
||||
<template #createTime="{ row }">
|
||||
<span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
</template>
|
||||
<template #receiveTime="{ row }">
|
||||
<span>{{ dayjs(row.receiveTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:sms-channel:update']"
|
||||
@click="handleDetail(row)"
|
||||
>
|
||||
<Icon icon="ep:view" class="mr-5px" /> {{ t('action.detail') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</Table>
|
||||
</ContentWrap>
|
||||
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle">
|
||||
<!-- 对话框(详情) -->
|
||||
<Descriptions
|
||||
v-if="actionType === 'detail'"
|
||||
:schema="allSchemas.detailSchema"
|
||||
:data="detailRef"
|
||||
>
|
||||
<template #code="{ row }">
|
||||
<DictTag :type="DICT_TYPE.SYSTEM_SMS_CHANNEL_CODE" :value="row.code" />
|
||||
</template>
|
||||
<template #status="{ row }">
|
||||
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
|
||||
</template>
|
||||
<template #createTime="{ row }">
|
||||
<span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
</template>
|
||||
</Descriptions>
|
||||
<!-- 操作按钮 -->
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">{{ t('dialog.close') }}</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
114
yudao-ui-admin-vue3/src/views/system/sms/smsLog/sms.log.data.ts
Normal file
114
yudao-ui-admin-vue3/src/views/system/sms/smsLog/sms.log.data.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
import { reactive } from 'vue'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { CrudSchema, useCrudSchemas } from '@/hooks/web/useCrudSchemas'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
// CrudSchema
|
||||
const crudSchemas = reactive<CrudSchema[]>([
|
||||
{
|
||||
label: t('common.index'),
|
||||
field: 'id',
|
||||
type: 'index',
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '手机号',
|
||||
field: 'mobile',
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '短信内容',
|
||||
field: 'templateContent'
|
||||
},
|
||||
{
|
||||
label: '短信渠道',
|
||||
field: 'channelId',
|
||||
dictType: DICT_TYPE.SYSTEM_SMS_CHANNEL_CODE,
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '发送状态',
|
||||
field: 'sendStatus',
|
||||
dictType: DICT_TYPE.SYSTEM_SMS_SEND_STATUS,
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '接收状态',
|
||||
field: 'receiveTime',
|
||||
dictType: DICT_TYPE.SYSTEM_SMS_RECEIVE_STATUS,
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '模板编号',
|
||||
field: 'templateId',
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '短信类型',
|
||||
field: 'channelId',
|
||||
dictType: DICT_TYPE.SYSTEM_SMS_TEMPLATE_TYPE,
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '接收时间',
|
||||
field: 'receiveTime',
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('common.createTime'),
|
||||
field: 'createTime',
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '接收时间',
|
||||
field: 'daterange',
|
||||
table: {
|
||||
show: false
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
search: {
|
||||
show: true,
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
type: 'daterange',
|
||||
valueFormat: 'YYYY-MM-DD'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('table.action'),
|
||||
field: 'action',
|
||||
width: '80px',
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
])
|
||||
export const { allSchemas } = useCrudSchemas(crudSchemas)
|
216
yudao-ui-admin-vue3/src/views/system/sms/smsTemplate/index.vue
Normal file
216
yudao-ui-admin-vue3/src/views/system/sms/smsTemplate/index.vue
Normal file
@@ -0,0 +1,216 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref, unref } from 'vue'
|
||||
import dayjs from 'dayjs'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { useTable } from '@/hooks/web/useTable'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { FormExpose } from '@/components/Form'
|
||||
import type { SmsTemplateVO } from '@/api/system/sms/smsTemplate/types'
|
||||
import { rules, allSchemas } from './sms.template.data'
|
||||
import * as SmsTemplateApi from '@/api/system/sms/smsTemplate'
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
// ========== 列表相关 ==========
|
||||
const { register, tableObject, methods } = useTable<PageResult<SmsTemplateVO>, SmsTemplateVO>({
|
||||
getListApi: SmsTemplateApi.getSmsTemplatePageApi,
|
||||
delListApi: SmsTemplateApi.deleteSmsTemplateApi
|
||||
})
|
||||
const { getList, setSearchParams, delList } = methods
|
||||
|
||||
// ========== CRUD 相关 ==========
|
||||
const loading = ref(false) // 遮罩层
|
||||
const actionType = ref('') // 操作按钮的类型
|
||||
const dialogVisible = ref(false) // 是否显示弹出层
|
||||
const dialogTitle = ref('edit') // 弹出层标题
|
||||
const formRef = ref<FormExpose>() // 表单 Ref
|
||||
|
||||
// 设置标题
|
||||
const setDialogTile = (type: string) => {
|
||||
dialogTitle.value = t('action.' + type)
|
||||
actionType.value = type
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
// 新增操作
|
||||
const handleCreate = () => {
|
||||
setDialogTile('create')
|
||||
// 重置表单
|
||||
unref(formRef)?.getElFormRef()?.resetFields()
|
||||
}
|
||||
|
||||
// 修改操作
|
||||
const handleUpdate = async (row: SmsTemplateVO) => {
|
||||
setDialogTile('update')
|
||||
// 设置数据
|
||||
const res = await SmsTemplateApi.getSmsTemplateApi(row.id)
|
||||
unref(formRef)?.setValues(res)
|
||||
}
|
||||
|
||||
// 提交按钮
|
||||
const submitForm = async () => {
|
||||
loading.value = true
|
||||
// 提交请求
|
||||
try {
|
||||
const data = unref(formRef)?.formModel as SmsTemplateVO
|
||||
if (actionType.value === 'create') {
|
||||
await SmsTemplateApi.createSmsTemplateApi(data)
|
||||
ElMessage.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await SmsTemplateApi.updateSmsTemplateApi(data)
|
||||
ElMessage.success(t('common.updateSuccess'))
|
||||
}
|
||||
// 操作成功,重新加载列表
|
||||
dialogVisible.value = false
|
||||
await getList()
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 删除操作
|
||||
const handleDelete = (row: SmsTemplateVO) => {
|
||||
delList(row.id, false)
|
||||
}
|
||||
|
||||
// ========== 详情相关 ==========
|
||||
const detailRef = ref() // 详情 Ref
|
||||
|
||||
// 详情操作
|
||||
const handleDetail = async (row: SmsTemplateVO) => {
|
||||
// 设置数据
|
||||
detailRef.value = row
|
||||
setDialogTile('detail')
|
||||
}
|
||||
const sendSmsForm = reactive({
|
||||
content: '',
|
||||
params: '',
|
||||
mobile: '',
|
||||
templateCode: '',
|
||||
templateParams: {}
|
||||
})
|
||||
// TODO 发送短信
|
||||
// ========== 测试相关 ==========
|
||||
const handleSendSms = (row: any) => {
|
||||
sendSmsForm.content = row.content
|
||||
sendSmsForm.params = row.params
|
||||
sendSmsForm.templateCode = row.code
|
||||
sendSmsForm.templateParams = row.params.reduce(function (obj, item) {
|
||||
obj[item] = undefined
|
||||
return obj
|
||||
}, {})
|
||||
}
|
||||
|
||||
// ========== 初始化 ==========
|
||||
getList()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 搜索工作区 -->
|
||||
<ContentWrap>
|
||||
<Search :schema="allSchemas.searchSchema" @search="setSearchParams" @reset="setSearchParams" />
|
||||
</ContentWrap>
|
||||
<ContentWrap>
|
||||
<!-- 操作工具栏 -->
|
||||
<div class="mb-10px">
|
||||
<el-button type="primary" v-hasPermi="['system:sms-channel:create']" @click="handleCreate">
|
||||
<Icon icon="el:zoom-in" class="mr-5px" /> {{ t('action.add') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<!-- 列表 -->
|
||||
<Table
|
||||
:columns="allSchemas.tableColumns"
|
||||
:selection="false"
|
||||
:data="tableObject.tableList"
|
||||
:loading="tableObject.loading"
|
||||
:pagination="{
|
||||
total: tableObject.total
|
||||
}"
|
||||
v-model:pageSize="tableObject.pageSize"
|
||||
v-model:currentPage="tableObject.currentPage"
|
||||
@register="register"
|
||||
>
|
||||
<template #type="{ row }">
|
||||
<DictTag :type="DICT_TYPE.SYSTEM_SMS_TEMPLATE_TYPE" :value="row.type" />
|
||||
</template>
|
||||
<template #status="{ row }">
|
||||
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
|
||||
</template>
|
||||
<template #createTime="{ row }">
|
||||
<span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:sms-template:send-sms']"
|
||||
@click="handleSendSms(row)"
|
||||
>
|
||||
<Icon icon="ep:cpu" class="mr-5px" /> {{ t('action.test') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:sms-template:update']"
|
||||
@click="handleUpdate(row)"
|
||||
>
|
||||
<Icon icon="ep:edit" class="mr-5px" /> {{ t('action.edit') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:sms-template:update']"
|
||||
@click="handleDetail(row)"
|
||||
>
|
||||
<Icon icon="ep:view" class="mr-5px" /> {{ t('action.detail') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:sms-template:delete']"
|
||||
@click="handleDelete(row)"
|
||||
>
|
||||
<Icon icon="ep:delete" class="mr-5px" /> {{ t('action.del') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</Table>
|
||||
</ContentWrap>
|
||||
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle">
|
||||
<!-- 对话框(添加 / 修改) -->
|
||||
<Form
|
||||
v-if="['create', 'update'].includes(actionType)"
|
||||
:schema="allSchemas.formSchema"
|
||||
:rules="rules"
|
||||
ref="formRef"
|
||||
/>
|
||||
<!-- 对话框(详情) -->
|
||||
<Descriptions
|
||||
v-if="actionType === 'detail'"
|
||||
:schema="allSchemas.detailSchema"
|
||||
:data="detailRef"
|
||||
>
|
||||
<template #type="{ row }">
|
||||
<DictTag :type="DICT_TYPE.SYSTEM_SMS_TEMPLATE_TYPE" :value="row.code" />
|
||||
</template>
|
||||
<template #status="{ row }">
|
||||
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
|
||||
</template>
|
||||
<template #createTime="{ row }">
|
||||
<span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
</template>
|
||||
</Descriptions>
|
||||
<!-- 操作按钮 -->
|
||||
<template #footer>
|
||||
<el-button
|
||||
v-if="['create', 'update'].includes(actionType)"
|
||||
type="primary"
|
||||
:loading="loading"
|
||||
@click="submitForm"
|
||||
>
|
||||
{{ t('action.save') }}
|
||||
</el-button>
|
||||
<el-button @click="dialogVisible = false">{{ t('dialog.close') }}</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
@@ -0,0 +1,115 @@
|
||||
import { reactive } from 'vue'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { required } from '@/utils/formRules'
|
||||
import { CrudSchema, useCrudSchemas } from '@/hooks/web/useCrudSchemas'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
// 表单校验
|
||||
export const rules = reactive({
|
||||
type: [required],
|
||||
name: [required],
|
||||
content: [required],
|
||||
apiTemplateId: [required],
|
||||
channelId: [required],
|
||||
code: [required],
|
||||
sort: [required],
|
||||
status: [required]
|
||||
})
|
||||
|
||||
// CrudSchema
|
||||
const crudSchemas = reactive<CrudSchema[]>([
|
||||
{
|
||||
label: t('common.index'),
|
||||
field: 'id',
|
||||
type: 'index',
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '模板编码',
|
||||
field: 'code',
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '模板名称',
|
||||
field: 'name',
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '模板内容',
|
||||
field: 'content'
|
||||
},
|
||||
{
|
||||
label: '短信 API 的模板编号',
|
||||
field: 'apiTemplateId',
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '短信类型',
|
||||
field: 'type',
|
||||
dictType: DICT_TYPE.SYSTEM_SMS_TEMPLATE_TYPE
|
||||
},
|
||||
{
|
||||
label: t('common.status'),
|
||||
field: 'status',
|
||||
dictType: DICT_TYPE.COMMON_STATUS
|
||||
},
|
||||
{
|
||||
label: t('form.remark'),
|
||||
field: 'remark',
|
||||
table: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('common.createTime'),
|
||||
field: 'createTime',
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('common.createTime'),
|
||||
field: 'daterange',
|
||||
table: {
|
||||
show: false
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
},
|
||||
search: {
|
||||
show: true,
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
type: 'daterange',
|
||||
valueFormat: 'YYYY-MM-DD'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('table.action'),
|
||||
field: 'action',
|
||||
width: '320px',
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
])
|
||||
export const { allSchemas } = useCrudSchemas(crudSchemas)
|
254
yudao-ui-admin-vue3/src/views/system/tenant/index.vue
Normal file
254
yudao-ui-admin-vue3/src/views/system/tenant/index.vue
Normal file
@@ -0,0 +1,254 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, unref, onMounted } from 'vue'
|
||||
import dayjs from 'dayjs'
|
||||
import { ElMessage, ElTag, ElSelect, ElOption } from 'element-plus'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { useTable } from '@/hooks/web/useTable'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { FormExpose } from '@/components/Form'
|
||||
import type { TenantVO } from '@/api/system/tenant/types'
|
||||
import { rules, allSchemas } from './tenant.data'
|
||||
import * as TenantApi from '@/api/system/tenant'
|
||||
import { getTenantPackageList } from '@/api/system/tenantPackage'
|
||||
import { TenantPackageVO } from '@/api/system/tenantPackage/types'
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
// ========== 列表相关 ==========
|
||||
const { register, tableObject, methods } = useTable<PageResult<TenantVO>, TenantVO>({
|
||||
getListApi: TenantApi.getTenantPageApi,
|
||||
delListApi: TenantApi.deleteTenantApi,
|
||||
exportListApi: TenantApi.exportTenantApi
|
||||
})
|
||||
const { getList, setSearchParams, delList, exportList } = methods
|
||||
|
||||
// 导出操作
|
||||
const handleExport = async () => {
|
||||
await exportList('租户数据.xls')
|
||||
}
|
||||
// ========== 套餐 ==========
|
||||
const tenantPackageId = ref() // 套餐
|
||||
const tenantPackageOptions = ref<TenantPackageVO[]>([]) //套餐列表
|
||||
|
||||
const getTenantPackageOptions = async () => {
|
||||
const res = await getTenantPackageList()
|
||||
tenantPackageOptions.value.push(...res)
|
||||
}
|
||||
const getPackageName = (packageId: number) => {
|
||||
for (let item of tenantPackageOptions.value) {
|
||||
if (item.id === packageId) {
|
||||
return item.name
|
||||
}
|
||||
}
|
||||
return '未知套餐'
|
||||
}
|
||||
// ========== CRUD 相关 ==========
|
||||
const actionLoading = ref(false) // 遮罩层
|
||||
const actionType = ref('') // 操作按钮的类型
|
||||
const dialogVisible = ref(false) // 是否显示弹出层
|
||||
const dialogTitle = ref('edit') // 弹出层标题
|
||||
const formRef = ref<FormExpose>() // 表单 Ref
|
||||
|
||||
// 设置标题
|
||||
const setDialogTile = (type: string) => {
|
||||
dialogTitle.value = t('action.' + type)
|
||||
actionType.value = type
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
// 新增操作
|
||||
const handleCreate = () => {
|
||||
setDialogTile('create')
|
||||
// 重置表单
|
||||
tenantPackageId.value = ''
|
||||
unref(formRef)?.getElFormRef()?.resetFields()
|
||||
}
|
||||
|
||||
// 修改操作
|
||||
const handleUpdate = async (row: any) => {
|
||||
setDialogTile('update')
|
||||
// 设置数据
|
||||
const res = await TenantApi.getTenantApi(row.id)
|
||||
tenantPackageId.value = res.packageId
|
||||
res.expireTime = dayjs(res.expireTime).format('YYYY-MM-DD HH:mm:ss')
|
||||
unref(formRef)?.setValues(res)
|
||||
}
|
||||
|
||||
// 提交按钮
|
||||
const submitForm = async () => {
|
||||
actionLoading.value = true
|
||||
// 提交请求 unix()
|
||||
try {
|
||||
const data = unref(formRef)?.formModel as TenantVO
|
||||
data.packageId = tenantPackageId.value
|
||||
if (actionType.value === 'create') {
|
||||
data.expireTime = dayjs(data.expireTime).valueOf().toString()
|
||||
await TenantApi.createTenantApi(data)
|
||||
ElMessage.success(t('common.createSuccess'))
|
||||
} else {
|
||||
data.expireTime = dayjs(data.expireTime).valueOf().toString()
|
||||
await TenantApi.updateTenantApi(data)
|
||||
ElMessage.success(t('common.updateSuccess'))
|
||||
}
|
||||
// 操作成功,重新加载列表
|
||||
dialogVisible.value = false
|
||||
await getList()
|
||||
} finally {
|
||||
actionLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 删除操作
|
||||
const handleDelete = (row: TenantVO) => {
|
||||
delList(row.id, false)
|
||||
}
|
||||
|
||||
// ========== 详情相关 ==========
|
||||
const detailRef = ref() // 详情 Ref
|
||||
|
||||
// 详情操作
|
||||
const handleDetail = async (row: any) => {
|
||||
// 设置数据
|
||||
detailRef.value = row
|
||||
setDialogTile('detail')
|
||||
}
|
||||
|
||||
// ========== 初始化 ==========
|
||||
onMounted(async () => {
|
||||
await getList()
|
||||
await getTenantPackageOptions()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 搜索工作区 -->
|
||||
<ContentWrap>
|
||||
<Search :schema="allSchemas.searchSchema" @search="setSearchParams" @reset="setSearchParams" />
|
||||
</ContentWrap>
|
||||
<ContentWrap>
|
||||
<!-- 操作工具栏 -->
|
||||
<div class="mb-10px">
|
||||
<el-button type="primary" v-hasPermi="['system:tenant:create']" @click="handleCreate">
|
||||
<Icon icon="el:zoom-in" class="mr-5px" /> {{ t('action.add') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
v-hasPermi="['system:tenant:export']"
|
||||
:loading="tableObject.exportLoading"
|
||||
@click="handleExport"
|
||||
>
|
||||
<Icon icon="ep:download" class="mr-5px" /> {{ t('action.export') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<!-- 列表 -->
|
||||
<Table
|
||||
:columns="allSchemas.tableColumns"
|
||||
:selection="false"
|
||||
:data="tableObject.tableList"
|
||||
:loading="tableObject.loading"
|
||||
:pagination="{
|
||||
total: tableObject.total
|
||||
}"
|
||||
v-model:pageSize="tableObject.pageSize"
|
||||
v-model:currentPage="tableObject.currentPage"
|
||||
@register="register"
|
||||
>
|
||||
<template #accountCount="{ row }">
|
||||
<el-tag> {{ row.accountCount }} </el-tag>
|
||||
</template>
|
||||
<template #status="{ row }">
|
||||
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
|
||||
</template>
|
||||
<template #packageId="{ row }">
|
||||
<!-- <DictTag :type="DICT_TYPE.SYSTEM_TENANT_PACKAGE_ID" :value="row.packageId" />-->
|
||||
<el-tag v-if="row.packageId === 0" type="danger">系统租户</el-tag>
|
||||
<el-tag v-else type="success"> {{ getPackageName(row.packageId) }} </el-tag>
|
||||
</template>
|
||||
<template #expireTime="{ row }">
|
||||
<span>{{ dayjs(row.expireTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
</template>
|
||||
<template #createTime="{ row }">
|
||||
<span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<!-- <el-button type="text" v-hasPermi="['system:tenant:update']" @click="handleUpdate(row)">-->
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:tenant:update']"
|
||||
@click="handleUpdate(row)"
|
||||
>
|
||||
<Icon icon="ep:edit" class="mr-5px" /> {{ t('action.edit') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:tenant:update']"
|
||||
@click="handleDetail(row)"
|
||||
>
|
||||
<Icon icon="ep:view" class="mr-5px" /> {{ t('action.detail') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:tenant:delete']"
|
||||
@click="handleDelete(row)"
|
||||
>
|
||||
<Icon icon="ep:delete" class="mr-5px" /> {{ t('action.del') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</Table>
|
||||
</ContentWrap>
|
||||
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle">
|
||||
<!-- 对话框(添加 / 修改) -->
|
||||
<Form
|
||||
v-if="['create', 'update'].includes(actionType)"
|
||||
:schema="allSchemas.formSchema"
|
||||
:rules="rules"
|
||||
ref="formRef"
|
||||
>
|
||||
<template #packageId>
|
||||
<el-select v-model="tenantPackageId" placeholder="Select">
|
||||
<el-option
|
||||
v-for="item in tenantPackageOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
</Form>
|
||||
<!-- 对话框(详情) -->
|
||||
<Descriptions
|
||||
v-if="actionType === 'detail'"
|
||||
:schema="allSchemas.detailSchema"
|
||||
:data="detailRef"
|
||||
>
|
||||
<template #status="{ row }">
|
||||
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
|
||||
</template>
|
||||
<template #packageId="{ row }">
|
||||
<el-tag v-if="row.packageId === 0" type="danger">系统租户</el-tag>
|
||||
<el-tag v-else type="success"> {{ getPackageName(row.packageId) }} </el-tag>
|
||||
</template>
|
||||
<template #expireTime="{ row }">
|
||||
<span>{{ dayjs(row.expireTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
</template>
|
||||
<template #createTime="{ row }">
|
||||
<span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
</template>
|
||||
</Descriptions>
|
||||
<!-- 操作按钮 -->
|
||||
<template #footer>
|
||||
<el-button
|
||||
v-if="['create', 'update'].includes(actionType)"
|
||||
type="primary"
|
||||
:loading="actionLoading"
|
||||
@click="submitForm"
|
||||
>
|
||||
{{ t('action.save') }}
|
||||
</el-button>
|
||||
<el-button @click="dialogVisible = false">{{ t('dialog.close') }}</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
109
yudao-ui-admin-vue3/src/views/system/tenant/tenant.data.ts
Normal file
109
yudao-ui-admin-vue3/src/views/system/tenant/tenant.data.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
import { reactive } from 'vue'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { required } from '@/utils/formRules'
|
||||
import { CrudSchema, useCrudSchemas } from '@/hooks/web/useCrudSchemas'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
// 表单校验
|
||||
export const rules = reactive({
|
||||
name: [required],
|
||||
packageId: [required],
|
||||
contactName: [required],
|
||||
contactMobile: [required],
|
||||
accountCount: [required],
|
||||
expireTime: [required],
|
||||
domain: [required],
|
||||
status: [required]
|
||||
})
|
||||
|
||||
// CrudSchema.
|
||||
const crudSchemas = reactive<CrudSchema[]>([
|
||||
{
|
||||
label: t('common.index'),
|
||||
field: 'id',
|
||||
type: 'index',
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '租户名称',
|
||||
field: 'name',
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '租户套餐',
|
||||
field: 'packageId'
|
||||
},
|
||||
{
|
||||
label: '联系人',
|
||||
field: 'contactName',
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '联系手机',
|
||||
field: 'contactMobile',
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '账号额度',
|
||||
field: 'accountCount',
|
||||
form: {
|
||||
component: 'InputNumber',
|
||||
value: 0
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '过期时间',
|
||||
field: 'expireTime',
|
||||
form: {
|
||||
show: true,
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
type: 'datetime',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '绑定域名',
|
||||
field: 'domain'
|
||||
},
|
||||
{
|
||||
label: '租户状态',
|
||||
field: 'status',
|
||||
dictType: DICT_TYPE.COMMON_STATUS,
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('table.createTime'),
|
||||
field: 'createTime',
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('table.action'),
|
||||
field: 'action',
|
||||
width: '240px',
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
])
|
||||
export const { allSchemas } = useCrudSchemas(crudSchemas)
|
202
yudao-ui-admin-vue3/src/views/system/tenantPackage/index.vue
Normal file
202
yudao-ui-admin-vue3/src/views/system/tenantPackage/index.vue
Normal file
@@ -0,0 +1,202 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, unref } from 'vue'
|
||||
import dayjs from 'dayjs'
|
||||
import { handleTree } from '@/utils/tree'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { useTable } from '@/hooks/web/useTable'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { FormExpose } from '@/components/Form'
|
||||
import { TenantPackageVO } from '@/api/system/tenantPackage/types'
|
||||
import { ElMessage, ElCard, ElCheckbox, ElTree } from 'element-plus'
|
||||
import { rules, allSchemas } from './tenantPackage.data'
|
||||
import * as TenantPackageApi from '@/api/system/tenantPackage'
|
||||
import { listSimpleMenusApi } from '@/api/system/menu'
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const defaultProps = {
|
||||
children: 'children',
|
||||
label: 'name',
|
||||
value: 'id'
|
||||
}
|
||||
// ========== 创建菜单树结构 ==========
|
||||
const menuOptions = ref([]) // 树形结构
|
||||
const treeRef = ref<InstanceType<typeof ElTree>>()
|
||||
const getTree = async () => {
|
||||
const res = await listSimpleMenusApi()
|
||||
menuOptions.value = handleTree(res)
|
||||
}
|
||||
let menuCheckStrictly = true
|
||||
const menuExpand = ref(false)
|
||||
const menuNodeAll = ref(false)
|
||||
|
||||
// ========== 列表相关 ==========
|
||||
const { register, tableObject, methods } = useTable<PageResult<TenantPackageVO>, TenantPackageVO>({
|
||||
getListApi: TenantPackageApi.getTenantPackageTypePageApi,
|
||||
delListApi: TenantPackageApi.deleteTenantPackageTypeApi
|
||||
})
|
||||
const { getList, setSearchParams, delList } = methods
|
||||
|
||||
// ========== CRUD 相关 ==========
|
||||
const loading = ref(false) // 遮罩层
|
||||
const formRef = ref<FormExpose>() // 表单 Ref
|
||||
const actionType = ref('') // 操作按钮的类型
|
||||
const dialogVisible = ref(false) // 是否显示弹出层
|
||||
const dialogTitle = ref('edit') // 弹出层标题
|
||||
const menuParentId = ref()
|
||||
|
||||
// 设置标题
|
||||
const setDialogTile = (type: string) => {
|
||||
dialogTitle.value = t('action.' + type)
|
||||
actionType.value = type
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
// 新增操作
|
||||
const handleCreate = () => {
|
||||
setDialogTile('create')
|
||||
// 重置表单
|
||||
unref(formRef)?.getElFormRef()?.resetFields()
|
||||
//重置菜单树
|
||||
unref(treeRef)?.setCheckedKeys([])
|
||||
menuCheckStrictly = true
|
||||
menuExpand.value = false
|
||||
menuNodeAll.value = false
|
||||
}
|
||||
|
||||
// 修改操作
|
||||
const handleUpdate = async (row: any) => {
|
||||
setDialogTile('update')
|
||||
// 设置数据
|
||||
const res = await TenantPackageApi.getTenantPackageApi(row.id)
|
||||
unref(formRef)?.setValues(res)
|
||||
// 设置菜单项
|
||||
// 设置为严格,避免设置父节点自动选中子节点,解决半选中问题
|
||||
menuCheckStrictly = true
|
||||
// 设置选中
|
||||
unref(treeRef)?.setCheckedKeys(res.menuIds)
|
||||
// 设置为非严格,继续使用半选中
|
||||
menuCheckStrictly = false
|
||||
}
|
||||
// 提交按钮
|
||||
const submitForm = async () => {
|
||||
loading.value = true
|
||||
// 提交请求
|
||||
try {
|
||||
const data = unref(formRef)?.formModel as TenantPackageVO
|
||||
data.menuIds = treeRef.value!.getCheckedKeys(false) as string[]
|
||||
if (actionType.value === 'create') {
|
||||
await TenantPackageApi.createTenantPackageTypeApi(data)
|
||||
ElMessage.success(t('common.createSuccess'))
|
||||
console.log('new data')
|
||||
} else {
|
||||
await TenantPackageApi.updateTenantPackageTypeApi(data)
|
||||
ElMessage.success(t('common.updateSuccess'))
|
||||
console.log('edit data')
|
||||
}
|
||||
// 操作成功,重新加载列表
|
||||
dialogVisible.value = false
|
||||
await getList()
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 删除操作
|
||||
const handleDelete = (row: TenantPackageVO) => {
|
||||
delList(row.id, false)
|
||||
}
|
||||
|
||||
// ========== 初始化 ==========
|
||||
onMounted(async () => {
|
||||
await getList()
|
||||
await getTree()
|
||||
})
|
||||
// getList()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 搜索工作区 -->
|
||||
<ContentWrap>
|
||||
<Search :schema="allSchemas.searchSchema" @search="setSearchParams" @reset="setSearchParams" />
|
||||
</ContentWrap>
|
||||
<ContentWrap>
|
||||
<!-- 操作工具栏 -->
|
||||
<div class="mb-10px">
|
||||
<el-button type="primary" @click="handleCreate">
|
||||
<Icon icon="el:zoom-in" class="mr-5px" /> {{ t('action.add') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<!-- 列表 -->
|
||||
<Table
|
||||
:columns="allSchemas.tableColumns"
|
||||
:selection="false"
|
||||
:data="tableObject.tableList"
|
||||
:loading="tableObject.loading"
|
||||
:pagination="{
|
||||
total: tableObject.total
|
||||
}"
|
||||
v-model:pageSize="tableObject.pageSize"
|
||||
v-model:currentPage="tableObject.currentPage"
|
||||
@register="register"
|
||||
>
|
||||
<template #status="{ row }">
|
||||
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
|
||||
</template>
|
||||
<template #createTime="{ row }">
|
||||
<span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<el-button link type="primary" @click="handleUpdate(row)">
|
||||
<Icon icon="ep:edit" class="mr-5px" /> {{ t('action.edit') }}
|
||||
</el-button>
|
||||
<el-button link type="primary" @click="handleDelete(row)">
|
||||
<Icon icon="ep:delete" class="mr-5px" /> {{ t('action.del') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</Table>
|
||||
</ContentWrap>
|
||||
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle" maxHeight="500px" width="50%">
|
||||
<!-- 对话框(添加 / 修改) -->
|
||||
<Form
|
||||
v-if="['create', 'update'].includes(actionType)"
|
||||
:schema="allSchemas.formSchema"
|
||||
:rules="rules"
|
||||
ref="formRef"
|
||||
>
|
||||
<template #menuIds>
|
||||
<el-card class="box-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<el-checkbox>展开/折叠</el-checkbox>
|
||||
<el-checkbox>全选/全不选</el-checkbox>
|
||||
</div>
|
||||
</template>
|
||||
<!-- default-expand-all-->
|
||||
<el-tree
|
||||
ref="treeRef"
|
||||
node-key="id"
|
||||
v-model="menuParentId"
|
||||
:props="defaultProps"
|
||||
:data="menuOptions"
|
||||
show-checkbox
|
||||
:check-strictly="menuCheckStrictly"
|
||||
empty-text="加载菜单中..."
|
||||
/>
|
||||
</el-card>
|
||||
</template>
|
||||
</Form>
|
||||
<!-- 操作按钮 -->
|
||||
<template #footer>
|
||||
<el-button
|
||||
v-if="['create', 'update'].includes(actionType)"
|
||||
type="primary"
|
||||
:loading="loading"
|
||||
@click="submitForm"
|
||||
>
|
||||
{{ t('action.save') }}
|
||||
</el-button>
|
||||
<el-button @click="dialogVisible = false">{{ t('dialog.close') }}</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
@@ -0,0 +1,89 @@
|
||||
import { reactive } from 'vue'
|
||||
import { required } from '@/utils/formRules'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { CrudSchema, useCrudSchemas } from '@/hooks/web/useCrudSchemas'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
// 表单校验
|
||||
export const rules = reactive({
|
||||
name: [required],
|
||||
id: [required],
|
||||
type: [required],
|
||||
remark: [required],
|
||||
status: [required],
|
||||
menuIds: [required]
|
||||
})
|
||||
|
||||
// CrudSchema
|
||||
const crudSchemas = reactive<CrudSchema[]>([
|
||||
{
|
||||
label: t('common.index'),
|
||||
field: 'id',
|
||||
type: 'index',
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '套餐名称',
|
||||
field: 'name',
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('common.status'),
|
||||
field: 'status',
|
||||
dictType: DICT_TYPE.COMMON_STATUS,
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '菜单权限',
|
||||
field: 'menuIds',
|
||||
table: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('form.remark'),
|
||||
field: 'remark',
|
||||
form: {
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
type: 'textarea',
|
||||
rows: 4
|
||||
},
|
||||
colProps: {
|
||||
span: 24
|
||||
}
|
||||
},
|
||||
table: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '创建时间',
|
||||
field: 'createTime',
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('table.action'),
|
||||
field: 'action',
|
||||
width: '240px',
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
])
|
||||
export const { allSchemas } = useCrudSchemas(crudSchemas)
|
475
yudao-ui-admin-vue3/src/views/system/user/index.vue
Normal file
475
yudao-ui-admin-vue3/src/views/system/user/index.vue
Normal file
@@ -0,0 +1,475 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, unref } from 'vue'
|
||||
import dayjs from 'dayjs'
|
||||
import {
|
||||
ElMessage,
|
||||
ElCard,
|
||||
ElTree,
|
||||
ElTreeSelect,
|
||||
ElSelect,
|
||||
ElOption,
|
||||
ElForm,
|
||||
ElFormItem,
|
||||
ElUpload,
|
||||
ElSwitch,
|
||||
ElCheckbox,
|
||||
ElMessageBox,
|
||||
UploadInstance,
|
||||
UploadRawFile
|
||||
} from 'element-plus'
|
||||
import { handleTree } from '@/utils/tree'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { useTable } from '@/hooks/web/useTable'
|
||||
import { FormExpose } from '@/components/Form'
|
||||
import type { UserVO } from '@/api/system/user/types'
|
||||
import type { PostVO } from '@/api/system/post/types'
|
||||
import { listSimpleDeptApi } from '@/api/system/dept'
|
||||
import { listSimplePostsApi } from '@/api/system/post'
|
||||
import { rules, allSchemas } from './user.data'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import download from '@/utils/download'
|
||||
import { useCache } from '@/hooks/web/useCache'
|
||||
import { CommonStatusEnum } from '@/utils/constants'
|
||||
const { wsCache } = useCache()
|
||||
interface Tree {
|
||||
id: number
|
||||
name: string
|
||||
children?: Tree[]
|
||||
}
|
||||
|
||||
const defaultProps = {
|
||||
children: 'children',
|
||||
label: 'name',
|
||||
value: 'id'
|
||||
}
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
// ========== 列表相关 ==========
|
||||
const tableTitle = ref('用户列表')
|
||||
const { register, tableObject, methods } = useTable<PageResult<UserVO>, UserVO>({
|
||||
getListApi: UserApi.getUserPageApi,
|
||||
delListApi: UserApi.deleteUserApi,
|
||||
exportListApi: UserApi.exportUserApi
|
||||
})
|
||||
const { getList, setSearchParams, delList, exportList } = methods
|
||||
|
||||
// ========== 创建部门树结构 ==========
|
||||
const deptOptions = ref([]) // 树形结构
|
||||
const searchForm = ref<FormExpose>()
|
||||
const treeRef = ref<InstanceType<typeof ElTree>>()
|
||||
const getTree = async () => {
|
||||
const res = await listSimpleDeptApi()
|
||||
deptOptions.value.push(...handleTree(res))
|
||||
}
|
||||
const filterNode = (value: string, data: Tree) => {
|
||||
if (!value) return true
|
||||
return data.name.includes(value)
|
||||
}
|
||||
const handleDeptNodeClick = (data: { [key: string]: any }) => {
|
||||
tableObject.paramsObj.params = {
|
||||
deptId: data.id
|
||||
}
|
||||
tableTitle.value = data.name
|
||||
methods.getList()
|
||||
}
|
||||
|
||||
// ========== CRUD 相关 ==========
|
||||
const loading = ref(false) // 遮罩层
|
||||
const actionType = ref('') // 操作按钮的类型
|
||||
const dialogVisible = ref(false) // 是否显示弹出层
|
||||
const dialogTitle = ref('edit') // 弹出层标题
|
||||
const formRef = ref<FormExpose>() // 表单 Ref
|
||||
const deptId = ref(0) // 部门ID
|
||||
const postIds = ref<string[]>([]) // 岗位ID
|
||||
const postOptions = ref<PostVO[]>([]) //岗位列表
|
||||
|
||||
// 获取岗位列表
|
||||
const getPostOptions = async () => {
|
||||
const res = await listSimplePostsApi()
|
||||
postOptions.value.push(...res)
|
||||
}
|
||||
// 设置标题
|
||||
const setDialogTile = async (type: string) => {
|
||||
dialogTitle.value = t('action.' + type)
|
||||
actionType.value = type
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
// 新增操作
|
||||
const handleAdd = () => {
|
||||
setDialogTile('create')
|
||||
// 重置表单
|
||||
deptId.value = 0
|
||||
unref(formRef)?.getElFormRef()?.resetFields()
|
||||
}
|
||||
|
||||
// 修改操作
|
||||
const handleUpdate = async (row: UserVO) => {
|
||||
await setDialogTile('update')
|
||||
// 设置数据
|
||||
const res = await UserApi.getUserApi(row.id)
|
||||
deptId.value = res.deptId
|
||||
postIds.value = res.postIds
|
||||
unref(formRef)?.setValues(res)
|
||||
}
|
||||
|
||||
// 提交按钮
|
||||
const submitForm = async () => {
|
||||
loading.value = true
|
||||
// 提交请求
|
||||
try {
|
||||
const data = unref(formRef)?.formModel as UserVO
|
||||
data.deptId = deptId.value
|
||||
data.postIds = postIds.value
|
||||
if (actionType.value === 'create') {
|
||||
await UserApi.createUserApi(data)
|
||||
ElMessage.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await UserApi.updateUserApi(data)
|
||||
ElMessage.success(t('common.updateSuccess'))
|
||||
}
|
||||
// 操作成功,重新加载列表
|
||||
dialogVisible.value = false
|
||||
await getList()
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
// 改变用户状态操作
|
||||
const handleStatusChange = async (row: UserVO) => {
|
||||
const text = row.status === CommonStatusEnum.ENABLE ? '停用' : '启用'
|
||||
ElMessageBox.confirm('确认要"' + text + '""' + row.username + '"用户吗?', t('common.reminder'), {
|
||||
confirmButtonText: t('common.ok'),
|
||||
cancelButtonText: t('common.cancel'),
|
||||
type: 'warning'
|
||||
})
|
||||
.then(async () => {
|
||||
row.status =
|
||||
row.status === CommonStatusEnum.ENABLE ? CommonStatusEnum.DISABLE : CommonStatusEnum.ENABLE
|
||||
const res = await UserApi.updateUserStatusApi(row.id, row.status)
|
||||
console.info(res)
|
||||
ElMessage.success(text + '成功')
|
||||
await getList()
|
||||
})
|
||||
.catch(() => {
|
||||
row.status =
|
||||
row.status === CommonStatusEnum.ENABLE ? CommonStatusEnum.DISABLE : CommonStatusEnum.ENABLE
|
||||
})
|
||||
}
|
||||
// 删除操作
|
||||
const handleDelete = (row: UserVO) => {
|
||||
delList(row.id, false)
|
||||
}
|
||||
|
||||
// 导出操作
|
||||
const handleExport = async () => {
|
||||
await exportList('用户数据.xls')
|
||||
}
|
||||
|
||||
// ========== 详情相关 ==========
|
||||
const detailRef = ref()
|
||||
|
||||
// 详情操作
|
||||
const handleDetail = async (row: UserVO) => {
|
||||
// 设置数据
|
||||
detailRef.value = row
|
||||
await setDialogTile('detail')
|
||||
}
|
||||
// ========== 导入相关 ==========
|
||||
const importDialogVisible = ref(false)
|
||||
const uploadDisabled = ref(false)
|
||||
const importDialogTitle = ref('用户导入')
|
||||
const updateSupport = ref(0)
|
||||
let updateUrl = import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL + '/system/user/import'
|
||||
const uploadHeaders = ref()
|
||||
// 下载导入模版
|
||||
const handleImportTemp = async () => {
|
||||
const res = await UserApi.importUserTemplateApi()
|
||||
download.excel(res, '用户导入模版.xls')
|
||||
}
|
||||
// 文件上传之前判断
|
||||
const beforeExcelUpload = (file: UploadRawFile) => {
|
||||
const isExcel =
|
||||
file.type === 'application/vnd.ms-excel' ||
|
||||
file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
const isLt5M = file.size / 1024 / 1024 < 5
|
||||
if (!isExcel) ElMessage.error('上传文件只能是 xls / xlsx 格式!')
|
||||
if (!isLt5M) ElMessage.error('上传文件大小不能超过 5MB!')
|
||||
return isExcel && isLt5M
|
||||
}
|
||||
// 文件上传
|
||||
const uploadRef = ref<UploadInstance>()
|
||||
const submitFileForm = () => {
|
||||
uploadHeaders.value = {
|
||||
Authorization: 'Bearer ' + wsCache.get('ACCESS_TOKEN'),
|
||||
'tenant-id': wsCache.get('tenantId')
|
||||
}
|
||||
uploadDisabled.value = true
|
||||
uploadRef.value!.submit()
|
||||
}
|
||||
// 文件上传成功
|
||||
const handleFileSuccess = (response: any): void => {
|
||||
if (response.code !== 0) {
|
||||
ElMessage.error(response.msg)
|
||||
return
|
||||
}
|
||||
importDialogVisible.value = false
|
||||
uploadDisabled.value = false
|
||||
const data = response.data
|
||||
let text = '上传成功数量:' + data.createUsernames.length + ';'
|
||||
for (let username of data.createUsernames) {
|
||||
text += '< ' + username + ' >'
|
||||
}
|
||||
text += '更新成功数量:' + data.updateUsernames.length + ';'
|
||||
for (const username of data.updateUsernames) {
|
||||
text += '< ' + username + ' >'
|
||||
}
|
||||
text += '更新失败数量:' + Object.keys(data.failureUsernames).length + ';'
|
||||
for (const username in data.failureUsernames) {
|
||||
text += '< ' + username + ': ' + data.failureUsernames[username] + ' >'
|
||||
}
|
||||
ElMessageBox.alert(text)
|
||||
}
|
||||
// 文件数超出提示
|
||||
const handleExceed = (): void => {
|
||||
ElMessage.error('最多只能上传一个文件!')
|
||||
}
|
||||
// 上传错误提示
|
||||
const excelUploadError = (): void => {
|
||||
ElMessage.error('导入数据失败,请您重新上传!')
|
||||
}
|
||||
// ========== 初始化 ==========
|
||||
onMounted(async () => {
|
||||
await getTree()
|
||||
await getPostOptions()
|
||||
})
|
||||
getList()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex">
|
||||
<el-card class="w-1/5 user" :gutter="12" shadow="always">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>部门列表</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-tree
|
||||
ref="treeRef"
|
||||
node-key="id"
|
||||
default-expand-all
|
||||
:data="deptOptions"
|
||||
:props="defaultProps"
|
||||
:highlight-current="true"
|
||||
:filter-method="filterNode"
|
||||
:expand-on-click-node="false"
|
||||
@node-click="handleDeptNodeClick"
|
||||
/>
|
||||
</el-card>
|
||||
<!-- 搜索工作区 -->
|
||||
<el-card class="w-4/5 user" style="margin-left: 10px" :gutter="12" shadow="hover">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>{{ tableTitle }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<Search
|
||||
:schema="allSchemas.searchSchema"
|
||||
@search="setSearchParams"
|
||||
@reset="setSearchParams"
|
||||
ref="searchForm"
|
||||
/>
|
||||
<!-- 操作工具栏 -->
|
||||
<div class="mb-10px">
|
||||
<el-button type="primary" v-hasPermi="['system:user:create']" @click="handleAdd">
|
||||
<Icon icon="el:zoom-in" class="mr-5px" /> {{ t('action.add') }}
|
||||
</el-button>
|
||||
<el-button v-hasPermi="['system:user:import']" @click="importDialogVisible = true">
|
||||
<Icon icon="ep:upload" class="mr-5px" /> {{ t('action.import') }}
|
||||
</el-button>
|
||||
<el-button v-hasPermi="['system:user:export']" @click="handleExport">
|
||||
<Icon icon="ep:download" class="mr-5px" /> {{ t('action.export') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<!-- 列表 -->
|
||||
<Table
|
||||
:columns="allSchemas.tableColumns"
|
||||
:selection="false"
|
||||
:data="tableObject.tableList"
|
||||
:loading="tableObject.loading"
|
||||
:pagination="{
|
||||
total: tableObject.total
|
||||
}"
|
||||
v-model:pageSize="tableObject.pageSize"
|
||||
v-model:currentPage="tableObject.currentPage"
|
||||
@register="register"
|
||||
>
|
||||
<template #sex="{ row }">
|
||||
<DictTag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="row.sex" />
|
||||
</template>
|
||||
<template #status="{ row }">
|
||||
<el-switch
|
||||
v-model="row.status"
|
||||
:active-value="0"
|
||||
:inactive-value="1"
|
||||
@change="handleStatusChange(row)"
|
||||
/>
|
||||
</template>
|
||||
<template #loginDate="{ row }">
|
||||
<span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:user:update']"
|
||||
@click="handleUpdate(row)"
|
||||
>
|
||||
<Icon icon="ep:edit" class="mr-5px" /> {{ t('action.edit') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:user:update']"
|
||||
@click="handleDetail(row)"
|
||||
>
|
||||
<Icon icon="ep:view" class="mr-5px" /> {{ t('action.detail') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['system:user:delete']"
|
||||
@click="handleDelete(row)"
|
||||
>
|
||||
<Icon icon="ep:delete" class="mr-5px" /> {{ t('action.del') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</Table>
|
||||
</el-card>
|
||||
</div>
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle">
|
||||
<!-- 对话框(添加 / 修改) -->
|
||||
<Form
|
||||
v-if="['create', 'update'].includes(actionType)"
|
||||
:rules="rules"
|
||||
:schema="allSchemas.formSchema"
|
||||
ref="formRef"
|
||||
>
|
||||
<template #deptId>
|
||||
<el-tree-select
|
||||
node-key="id"
|
||||
v-model="deptId"
|
||||
:props="defaultProps"
|
||||
:data="deptOptions"
|
||||
check-strictly
|
||||
/>
|
||||
</template>
|
||||
<template #postIds>
|
||||
<el-select v-model="postIds" multiple placeholder="Select">
|
||||
<el-option
|
||||
v-for="item in postOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
</Form>
|
||||
<!-- 对话框(详情) -->
|
||||
<Descriptions
|
||||
v-if="actionType === 'detail'"
|
||||
:schema="allSchemas.detailSchema"
|
||||
:data="detailRef"
|
||||
>
|
||||
<template #deptId="{ row }">
|
||||
<span>{{ row.dept.name }}</span>
|
||||
</template>
|
||||
<template #postIds="{ row }">
|
||||
<span>{{ row.dept.name }}</span>
|
||||
</template>
|
||||
<template #sex="{ row }">
|
||||
<DictTag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="row.sex" />
|
||||
</template>
|
||||
<template #status="{ row }">
|
||||
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
|
||||
</template>
|
||||
<template #loginDate="{ row }">
|
||||
<span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
</template>
|
||||
</Descriptions>
|
||||
<!-- 操作按钮 -->
|
||||
<template #footer>
|
||||
<el-button
|
||||
v-if="['create', 'update'].includes(actionType)"
|
||||
type="primary"
|
||||
:loading="loading"
|
||||
@click="submitForm"
|
||||
>
|
||||
{{ t('action.save') }}
|
||||
</el-button>
|
||||
<el-button @click="dialogVisible = false">{{ t('dialog.close') }}</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
<Dialog
|
||||
v-model="importDialogVisible"
|
||||
:title="importDialogTitle"
|
||||
:destroy-on-close="true"
|
||||
maxHeight="350px"
|
||||
>
|
||||
<el-form class="drawer-multiColumn-form" label-width="150px">
|
||||
<el-form-item label="模板下载 :">
|
||||
<el-button type="primary" @click="handleImportTemp">
|
||||
<Icon icon="ep:download" />
|
||||
点击下载
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item label="文件上传 :">
|
||||
<el-upload
|
||||
ref="uploadRef"
|
||||
:action="updateUrl + '?updateSupport=' + updateSupport"
|
||||
:headers="uploadHeaders"
|
||||
:drag="true"
|
||||
:limit="1"
|
||||
:multiple="true"
|
||||
:show-file-list="true"
|
||||
:disabled="uploadDisabled"
|
||||
:before-upload="beforeExcelUpload"
|
||||
:on-exceed="handleExceed"
|
||||
:on-success="handleFileSuccess"
|
||||
:on-error="excelUploadError"
|
||||
:auto-upload="false"
|
||||
accept="application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
||||
>
|
||||
<Icon icon="ep:upload-filled" />
|
||||
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">请上传 .xls , .xlsx 标准格式文件</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否更新已经存在的用户数据:">
|
||||
<el-checkbox v-model="updateSupport" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button type="primary" @click="submitFileForm">
|
||||
<Icon icon="ep:upload-filled" />
|
||||
{{ t('action.save') }}
|
||||
</el-button>
|
||||
<el-button @click="importDialogVisible = false">{{ t('dialog.close') }}</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.user {
|
||||
height: 900px;
|
||||
max-height: 960px;
|
||||
}
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
134
yudao-ui-admin-vue3/src/views/system/user/user.data.ts
Normal file
134
yudao-ui-admin-vue3/src/views/system/user/user.data.ts
Normal file
@@ -0,0 +1,134 @@
|
||||
import { reactive } from 'vue'
|
||||
import { required } from '@/utils/formRules'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { CrudSchema, useCrudSchemas } from '@/hooks/web/useCrudSchemas'
|
||||
// 国际化
|
||||
const { t } = useI18n()
|
||||
// 表单校验
|
||||
export const rules = reactive({
|
||||
name: [required],
|
||||
code: [required],
|
||||
sort: [required],
|
||||
status: [required]
|
||||
})
|
||||
// crudSchemas
|
||||
const crudSchemas = reactive<CrudSchema[]>([
|
||||
{
|
||||
label: t('common.index'),
|
||||
field: 'id',
|
||||
type: 'index',
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '用户账号',
|
||||
field: 'username',
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '用户昵称',
|
||||
field: 'nickname'
|
||||
},
|
||||
{
|
||||
label: '用户邮箱',
|
||||
field: 'email'
|
||||
},
|
||||
{
|
||||
label: '手机号码',
|
||||
field: 'mobile',
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '用户性别',
|
||||
field: 'sex',
|
||||
dictType: DICT_TYPE.SYSTEM_USER_SEX
|
||||
},
|
||||
{
|
||||
label: '部门',
|
||||
field: 'deptId',
|
||||
table: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '岗位',
|
||||
field: 'postIds',
|
||||
table: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('common.status'),
|
||||
field: 'status',
|
||||
dictType: DICT_TYPE.COMMON_STATUS,
|
||||
search: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '最后登录时间',
|
||||
field: 'loginDate',
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '最后登录IP',
|
||||
field: 'loginIp',
|
||||
table: {
|
||||
show: false
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('form.remark'),
|
||||
field: 'remark',
|
||||
table: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('common.createTime'),
|
||||
field: 'daterange',
|
||||
table: {
|
||||
show: false
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
},
|
||||
search: {
|
||||
show: true,
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
type: 'daterange',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
width: '240px',
|
||||
label: t('table.action'),
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
])
|
||||
export const { allSchemas } = useCrudSchemas(crudSchemas)
|
Reference in New Issue
Block a user