提交 503f411b authored 作者: xiejiang's avatar xiejiang

feat: 新增权限克隆 以及优化菜单,表单等多个优化项

上级 450cbfe6
......@@ -2,7 +2,7 @@
* @Author: xiejiang
* @Date: 2024-12-02 14:56:33
* @LastEditors: xiejiang
* @LastEditTime: 2024-12-06 16:50:46
* @LastEditTime: 2024-12-08 14:51:30
* @Description:
* Copyright(c)2024 by 好老师教育科技有限公司 All right Reserved.
*/
......@@ -46,9 +46,9 @@ export interface ReqSaveDictionary {
dictCode: string // 字典编码
dictId: number | string // 字典ID
dictName: string // 字典名称
isAllowAddsub: number // 是否允许添加子集:1允许,0不允许
isAllowDelete: number // 是否允许删除:1允许,0不允许
isAllowEdit: number // 是否允许编辑:1允许,0不允许
isAllowAddsub: number | undefined // 是否允许添加子集:1允许,0不允许
isAllowDelete: number | undefined // 是否允许删除:1允许,0不允许
isAllowEdit: number | undefined // 是否允许编辑:1允许,0不允许
remark: string // 字典说明
}
......
差异被折叠。
......@@ -14,8 +14,10 @@ const hanldleClick = () => {
.back {
width: 40px;
height: 24px;
position: absolute;
text-align: center;
border-radius: 12px;
z-index: 999;
border: 1px solid var(--td-gray-color-2);
color: var(--td-font-gray-2);
font-size: 12px;
......
......@@ -2,7 +2,7 @@
* @Author: xiejiang
* @Date: 2024-11-28 09:24:26
* @LastEditors: xiejiang
* @LastEditTime: 2024-11-29 09:35:06
* @LastEditTime: 2024-12-08 15:47:14
* @Description: table操作按钮
* Copyright(c)2024 by 好老师教育科技有限公司 All right Reserved.
-->
......@@ -14,6 +14,7 @@
@click="handleClick"
:disabled="props.disabled"
:loading="props.loading"
v-bind="$attrs"
>
<template #icon> <slot></slot></template>
{{ props.content }}
......
......@@ -167,4 +167,11 @@ const onPageChange = (pageInfo: { current: number; pageSize: number }) => {
text-align: right;
padding-right: 20px;
}
.cell-content {
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
</style>
......@@ -2,7 +2,7 @@
* @Author: xiejiang
* @Date: 2024-11-29 16:08:58
* @LastEditors: xiejiang
* @LastEditTime: 2024-11-29 16:23:47
* @LastEditTime: 2024-12-08 15:08:34
* @Description:
* Copyright(c)2024 by 好老师教育科技有限公司 All right Reserved.
-->
......@@ -34,7 +34,7 @@ const props = defineProps({
display: inline-block;
width: 4px;
height: 16px;
border-radius: 2px;
border-radius: 0 3px;
background: var(--td-brand-color);
}
......
......@@ -2,7 +2,7 @@
* @Author: xiejiang
* @Date: 2024-11-11 09:38:40
* @LastEditors: xiejiang
* @LastEditTime: 2024-12-02 11:04:03
* @LastEditTime: 2024-12-08 11:40:59
* @Description:
* Copyright(c)2024 by 好老师教育科技有限公司 All right Reserved.
-->
......@@ -14,7 +14,7 @@
alt="logo"
:width="isCollapse ? 64 : 208"
:height="isCollapse ? 64 : 64"
:class="[isCollapse ? 'ml-[28px]' : 'ml-[10px]']"
class="ml-[0px] logo-transition"
@click="$router.push('/')"
/>
<Icon
......@@ -50,4 +50,10 @@ const changeCollapse = () => globalStore.setGlobalState('isCollapse', !globalSto
height: 60px;
color: var(--td-font-gray-1);
}
.logo-transition {
transition:
width 0.2s ease,
height 0.2s ease;
}
</style>
......@@ -11,6 +11,11 @@
max-height: calc(100% - 52px);
max-width: calc(100% - 24px);
overflow: auto;
> div {
min-width: 1200px;
overflow: auto;
}
}
.t-footer {
......
......@@ -2,7 +2,7 @@
* @Author: xiejiang
* @Date: 2024-11-11 09:38:40
* @LastEditors: xiejiang
* @LastEditTime: 2024-11-13 18:01:45
* @LastEditTime: 2024-12-08 16:25:46
* @Description:
* Copyright(c)2024 by 好老师教育科技有限公司 All right Reserved.
-->
......@@ -11,11 +11,32 @@
<div
v-show="isCollapse"
class="w-[32px] h-[32px] flex justify-center items-center bg-gray-1 rounded-[4px] cursor-pointer"
@click="handleCollapse"
>
<t-icon name="search" size="24" />
<t-tooltip placement="right" theme="light" overlay-class-name="custom-tooltip">
<template #content>
<div class="p-[16px]">
<t-auto-complete
ref="menuInputRef"
style="width: 240px"
v-model="searchMenu"
:options="options"
highlight-keyword
:filterable="false"
placeholder="请输入关键词搜索"
clearable
@change="onChange"
:on-select="handleClickMenu"
/>
</div>
</template>
<template #default>
<t-icon name="search" size="24" />
</template>
</t-tooltip>
</div>
<!-- <t-tooltip placement="top" theme="light" overlay-class-name="custom-tooltip"> </t-tooltip> -->
<t-auto-complete
ref="menuInputRef"
v-show="!isCollapse"
......@@ -68,9 +89,9 @@ const handleClickMenu = (label: string) => {
}
}
function handleCollapse() {
globalStore.setGlobalState('isCollapse', !globalStore.isCollapse)
}
// function handleCollapse() {
// globalStore.setGlobalState('isCollapse', !globalStore.isCollapse)
// }
</script>
<style lang="less" scoped>
......@@ -88,5 +109,9 @@ function handleCollapse() {
color: var(--font-gray-4);
}
}
:deep(.t-icon) {
color: var(--td-font-gray-6);
}
}
</style>
......@@ -2,12 +2,20 @@
* @Author: xiejiang
* @Date: 2024-11-11 09:38:40
* @LastEditors: xiejiang
* @LastEditTime: 2024-12-02 14:24:20
* @LastEditTime: 2024-12-08 11:25:18
* @Description:
* Copyright(c)2024 by 好老师教育科技有限公司 All right Reserved.
-->
<template>
<t-menu :collapsed="isCollapse" :value="activeMenu" expand-mutex v-model:expanded="expanded">
<t-menu
:collapsed="isCollapse"
width="208px"
:class="'collapse' + isCollapse ? 'active' : ''"
:value="activeMenu"
expand-mutex
:default-value="defaultMenu"
v-model:expanded="expanded"
>
<SubMenu :menu-list="menuList" />
</t-menu>
</template>
......@@ -25,10 +33,13 @@ const menuList = computed(() => authStore.authMenuListGet)
const activeMenu = computed(() => authStore.menuSign)
const isCollapse = computed(() => globalStore.isCollapse)
const expanded = ref<string[]>([])
const defaultMenu = ref('')
// 设置展开菜单
const expandedFunc = (name: string) => {
expanded.value = [menuList.value.find(item => item.children?.some(em => em.menuSign === name))?.menuSign] as string[]
console.log(expanded.value)
}
watch(activeMenu, val => {
......@@ -38,10 +49,57 @@ watch(activeMenu, val => {
onMounted(() => {
const name = route.name as string
expandedFunc(name)
defaultMenu.value = expanded.value[0]
})
</script>
<style lang="less">
.t-default-menu {
height: calc(100% - 112px) !important;
}
.t-default-menu.t-is-collapsed {
width: 52px !important;
}
.t-default-menu__inner .t-menu {
padding: var(--td-comp-paddingTB-l) 0;
}
.t-default-menu .t-menu__item.t-is-opened {
color: var(--td-brand-color) !important;
font-weight: 700;
.t-icon {
color: var(--td-brand-color);
}
}
.t-is-collapsed {
.t-menu__item {
border-radius: 0 8px 8px 0;
}
.t-is-opened {
color: var(--td-white);
background-color: var(--td-brand-color) !important;
border-radius: 0 8px 8px 0;
.t-icon {
color: var(--td-white) !important;
}
}
.t-menu__item.t-is-opened .t-icon {
color: var(--td-brand-color);
}
.t-menu__item.t-is-active:not(.t-is-opened) {
color: var(--td-white);
background-color: var(--td-brand-color) !important;
.t-icon {
color: var(--td-white) !important;
}
}
}
</style>
......@@ -2,7 +2,7 @@
* @Author: xiejiang
* @Date: 2024-11-11 09:38:40
* @LastEditors: xiejiang
* @LastEditTime: 2024-11-28 18:59:10
* @LastEditTime: 2024-12-08 10:41:05
* @Description:
* Copyright(c)2024 by 好老师教育科技有限公司 All right Reserved.
-->
......@@ -10,7 +10,7 @@
<t-layout class="layout-container">
<Header class="layout-header" />
<t-layout class="layout-content">
<t-aside :class="[isCollapse ? 'w-[96px]' : 'w-[232px]']">
<t-aside :class="[isCollapse ? 'w-[52px]' : 'w-[208px]']">
<MenuSearch />
<Menu />
<!-- <MiddleBtn /> -->
......@@ -30,7 +30,7 @@ import Menu from '@/layouts/components/Menu/index.vue'
const globalStore = useGlobalStore()
const isCollapse = computed(() => globalStore.isCollapse)
const tabWidth = isCollapse.value ? 'width: calc(100% - 96px)' : 'width: calc(100% - 232px)'
const tabWidth = isCollapse.value ? 'width: calc(100% - 52px)' : 'width: calc(100% - 208px)'
</script>
<style scoped lang="less">
......
......@@ -24,7 +24,8 @@ export interface UserInfo {
loginInfo: {
device: string
ip: string
location: string
location?: string
loginAddress?: string
time: string
}
status: string
......
......@@ -84,6 +84,7 @@
--td-font-gray-3: rgb(0 0 0 / 40%);
--td-font-gray-4: rgb(0 0 0 / 20%);
--td-font-gray-5: rgb(0 0 0 / 6%);
--td-font-gray-6: rgb(0 0 0 / 26%);
--td-white: #fff;
--td-border: var(--td-gray-color-2);
--td-text-color-primary: var(--td-font-gray-1);
......
......@@ -11,7 +11,7 @@
<Back />
<main class="m-auto max-w-[1012px]">
<!-- 基本信息 -->
<PageTitle title="基本信息" class="mb-[24px]" />
<PageTitle title="基本信息" class="mb-[24px] mt-[12px]" />
<div class="flex flex-wrap">
<DetailTable label="机构名称" :value="state?.companyName" />
<DetailTable label="机构类型" :value="state?.companyType" />
......
......@@ -2,7 +2,7 @@
* @Author: xiejiang
* @Date: 2024-11-28 15:01:02
* @LastEditors: xiejiang
* @LastEditTime: 2024-12-05 10:51:58
* @LastEditTime: 2024-12-08 14:43:35
* @Description: 机构管理
* Copyright(c)2024 by 好老师教育科技有限公司 All right Reserved.
-->
......@@ -18,7 +18,11 @@
@page-change="handlePage"
>
<template #header-left>
<SearchWithButton :loading="loading" placeholder="机构名称" v-model="condRoleName" @on-search="handleSearch"
<SearchWithButton
:loading="loading"
placeholder="机构名称"
v-model="companyName"
@on-search="handleSearch(searchParams)"
/></template>
<template #header-right>
......@@ -43,7 +47,7 @@
<script setup lang="tsx" name="institution">
import { useRouter } from 'vue-router'
import { PrimaryTableCol } from 'tdesign-vue-next'
import { PrimaryTableCol, MessagePlugin } from 'tdesign-vue-next'
import type { filterItemProp } from '@/components/PageFilters/interface/index'
import type { Pagination } from '@/components/PageContainer/interface/index'
import { statusTxt } from '@/utils/status'
......@@ -86,7 +90,7 @@ const pagination = ref({
total: 0
})
const condRoleName = ref('')
const companyName = ref('')
// 筛选项配置,type类型参考pageFilters组件下的ts定义
const filterConditions = ref<filterItemProp[]>([
......@@ -122,10 +126,11 @@ const getDictData = () => {
// 表格columns配置
const columns: PrimaryTableCol[] = [
{
title: '序号',
colKey: 'serial-number',
width: 50
width: 70
},
{ title: '机构名称', colKey: 'companyName', ellipsis: true },
{ title: '机构名称', colKey: 'companyName' },
{
title: '机构类型',
colKey: 'companyTypeName'
......@@ -171,7 +176,7 @@ const getData = () => {
const postData = {
...pagination.value,
...searchParams.value,
condRoleName: condRoleName.value
condRoleName: companyName.value
}
getCompanyList(postData)
.then(res => {
......@@ -182,7 +187,7 @@ const getData = () => {
loading.value = false
})
}
const searchParams = ref<Record<string, any>>({}) // 搜索筛选
const searchParams = ref<Record<string, any>>({}) // 搜索筛选
// 处理搜索条件
const handleSearch = (params: Record<string, any>) => {
......@@ -228,8 +233,12 @@ const handelStatus = (row: ResCompany, status: number) => {
})
.then(() => {
getData()
MessagePlugin.success('操作成功')
dialog.hide()
})
.catch(() => {
MessagePlugin.error('操作失败')
})
.finally(() => {
dialog.update({ confirmBtn: { content: '确定', loading: false } })
})
......@@ -247,9 +256,13 @@ const handelDel = (row: ResCompany) => {
companyId: row?.companyId
})
.then(() => {
MessagePlugin.success('操作成功')
getData()
dialog.hide()
})
.catch(() => {
MessagePlugin.error('操作失败')
})
.finally(() => {
dialog.update({ confirmBtn: { content: '确定', loading: false } })
})
......
......@@ -2,7 +2,7 @@
* @Author: xiejiang
* @Date: 2024-11-05 10:14:56
* @LastEditors: xiejiang
* @LastEditTime: 2024-12-02 11:08:02
* @LastEditTime: 2024-12-08 14:49:30
* @Description: 首页
* Copyright(c)2024 by 好老师教育科技有限公司 All right Reserved.
-->
......@@ -14,22 +14,25 @@
<div class="home-avatar flex items-center justify-center">
<div>
<p class="text-20 bold">{{ userInfo && userInfo.userName }},您好!</p>
<p class="text-14">欢迎使用好老师考研后台管理系统</p>
<p class="text-14">欢迎使用智慧教育平台</p>
</div>
</div>
<div class="home-line w-full"></div>
<div class="home-user flex flex-col items-center justify-center">
<p>
<span>部门:</span>
<span>{{ userInfo?.department }}</span>
</p>
<p>
<span>岗位:</span>
<span>{{ userInfo?.position }}</span>
<span>角色:</span>
<span class="max-w-[calc(100%-90px)]">
<span v-for="v in userInfo?.roleNames || []" :key="v"> {{ v }}</span></span
>
<span v-if="!userInfo?.roleNames.length">-</span>
</p>
<p>
<span>角色:</span>
<span>{{ userInfo?.roleName }}</span>
<span>上次登录:</span>
<span class="max-w-[calc(100%-90px)]"
>{{ userInfo?.loginInfo.loginAddress || '-' }}{{ userInfo?.loginInfo.time || '-' }}{{
userInfo?.loginInfo.device || '-'
}}</span
>
</p>
</div>
<img :src="homeImg" class="mt-[80px]" width="502" alt="" />
......@@ -90,7 +93,6 @@ onMounted(() => {
> p {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
font-weight: 400;
font-size: 14px;
......
......@@ -2,7 +2,7 @@
* @Author: xiejiang
* @Date: 2024-11-05 10:14:56
* @LastEditors: xiejiang
* @LastEditTime: 2024-12-05 16:40:21
* @LastEditTime: 2024-12-08 14:31:01
* @Description: 登录
* Copyright(c)2024 by 好老师教育科技有限公司 All right Reserved.
-->
......@@ -21,7 +21,12 @@
<t-form-item name="mobile" :required-mark="false">
<t-input
v-model="formData.mobile"
@input="v => (formData.mobile = v.replace(/[^0-9]/g, ''))"
maxlength="11"
@input="
v => {
formData.mobile = v.target.value.replace(/[^0-9]/g, '')
}
"
placeholder="请输入手机号"
>
<template #prefix-icon>
......@@ -30,7 +35,16 @@
</t-input>
</t-form-item>
<t-form-item name="code" :required-mark="false">
<t-input v-model="formData.code" placeholder="请输入验证码">
<t-input
v-model="formData.code"
maxlength="4"
@input="
v => {
formData.code = v.target.value.replace(/[^0-9]/g, '')
}
"
placeholder="请输入验证码"
>
<template #prefix-icon>
<ShieldErrorIcon />
</template>
......@@ -141,6 +155,9 @@ const isShowPuzzleVcode = ref(false)
const sliderVerify = ref()
// 发送短信验证码
const getCode = function () {
if (!isPhoneNumber(formData.mobile) || codeBtnDisabled.value) {
return
}
isShowPuzzleVcode.value = true
}
......@@ -168,7 +185,7 @@ const onPuzzleVcodeClose = () => {
telephone: formData.mobile
})
.then(() => {
MessagePlugin.success('已发送短信验证码!')
MessagePlugin.success('验证成功!')
// 重置密码的验证码
codeBtnTxt.value = '<span class="text-primary">60s</span>后重新获取'
codeBtnDisabled.value = true
......
......@@ -2,7 +2,7 @@
* @Author: xiejiang
* @Date: 2024-11-11 09:38:40
* @LastEditors: xiejiang
* @LastEditTime: 2024-12-05 16:55:50
* @LastEditTime: 2024-12-08 15:49:03
* @Description: 系统字典-管理子集
* Copyright(c)2024 by 好老师教育科技有限公司 All right Reserved.
-->
......@@ -11,7 +11,7 @@
<Back />
<main class="m-auto max-w-[1012px]">
<!-- 基本信息 -->
<PageTitle title="基本信息" class="my-[24px]" />
<PageTitle title="基本信息" class="mb-[24px] mt-[12px]" />
<div class="flex flex-wrap" v-loading="detailLoading">
<DetailTable label="字典大类" :value="dataInfo.categoryName" />
<DetailTable label="字典名称" :value="dataInfo.dictName" />
......@@ -40,6 +40,11 @@
<template v-for="(_, name) in $slots" #[name]="scope">
<slot :name="name" v-bind="scope" />
</template>
<template v-for="col in columns" #[col.colKey]="{ row }" :key="col.colKey">
<template v-if="col.colKey != 'operation'">
<div class="cell-content">{{ row[col.colKey] || '-' }}</div>
</template>
</template>
</t-table>
<div class="table-add">
<span class="cursor-pointer text-[var(--td-brand-color)]" @click="handleAdd">+ 新增 </span>
......@@ -75,7 +80,7 @@ const dataInfo = ref<ResDictionary>({
dictName: '',
dictCode: '',
remark: '',
dictId: 0,
dictId: '',
isAllowAddsub: 0,
isAllowDelete: 0
})
......@@ -118,21 +123,36 @@ const pagination = ref({
// 表格columns配置
const columns: PrimaryTableCol[] = [
{
title: '序号',
colKey: 'serial-number',
width: 50
width: 70
},
{ title: '值名称', colKey: 'valueName', ellipsis: true },
{ title: '值编码', colKey: 'dataCode', ellipsis: true },
{ title: '备注', colKey: 'remark', ellipsis: true },
{ title: '值名称', colKey: 'valueName' },
{ title: '值编码', colKey: 'dataCode' },
{ title: '备注', colKey: 'remark' },
{
title: '快速排序',
colKey: 'sort',
width: 88,
cell: (h, { row }: { row: any }) => {
cell: (h, { row, rowIndex }: { row: any; rowIndex: number }) => {
return (
<div class='flex items-center text-[var(--td-brand-color)]'>
<ArrowDownIcon class='mr-[8px] cursor-pointer' onClick={handleSort(row, true)} />
<ArrowUpIcon class='cursor-pointer' onClick={handleSort(row, false)} />
<operate-btn
content=''
class='ico-btn'
disabled={rowIndex == 0 ? true : false}
onClick={handleSort(row, false)}
>
<ArrowUpIcon />
</operate-btn>
<operate-btn
content=''
class='ico-btn'
disabled={rowIndex == tableData.value.length - 1 ? true : false}
onClick={handleSort(row, true)}
>
<ArrowDownIcon />
</operate-btn>
</div>
)
}
......@@ -186,8 +206,12 @@ const handleDel = (row: ResDictionary) => () => {
})
.then(() => {
GetDictDataList()
MessagePlugin.success('操作成功')
dialog.hide()
})
.catch(() => {
MessagePlugin.error('操作失败')
})
.finally(() => {
dialog.update({ confirmBtn: { content: '确定', loading: false } })
})
......@@ -220,7 +244,7 @@ const GetDictDataList = () => {
onMounted(() => {
setBreadcrumb([
{
title: '系统权限'
title: '系统配置'
},
{
title: '系统字典',
......@@ -236,8 +260,6 @@ onMounted(() => {
</script>
<style lang="less" scoped>
@import './comm.less';
.table-add {
width: 100%;
padding: 13px;
......@@ -245,4 +267,8 @@ onMounted(() => {
box-sizing: border-box;
background: var(--td-brand-color-1);
}
:deep(.ico-btn) {
padding: 0;
}
</style>
......@@ -2,7 +2,7 @@
* @Author: xiejiang
* @Date: 2024-11-11 09:38:40
* @LastEditors: xiejiang
* @LastEditTime: 2024-12-05 15:03:49
* @LastEditTime: 2024-12-08 11:34:20
* @Description: 系统字典
* Copyright(c)2024 by 好老师教育科技有限公司 All right Reserved.
-->
......@@ -50,7 +50,7 @@
<script setup lang="tsx" name="dictionary">
import { useRouter } from 'vue-router'
import { PrimaryTableCol } from 'tdesign-vue-next'
import { PrimaryTableCol, MessagePlugin } from 'tdesign-vue-next'
import type { filterItemProp } from '@/components/PageFilters/interface/index'
import type { Pagination } from '@/components/PageContainer/interface/index'
import { statusTxt, dictionaryAttributes, dictionaryAttributeTxt } from '@/utils/status'
......@@ -72,7 +72,7 @@ const router = useRouter()
onMounted(() => {
setBreadcrumb([
{
title: '系统权限'
title: '系统配置'
},
{
title: '系统字典'
......@@ -117,11 +117,12 @@ const filterConditions = computed<filterItemProp[]>(() => [
// 表格columns配置
const columns: PrimaryTableCol[] = [
{
title: '序号',
colKey: 'serial-number',
width: 50
width: 70
},
{ title: '字典大类', colKey: 'categoryId', ellipsis: true },
{ title: '字典名称', colKey: 'dictName', ellipsis: true },
{ title: '字典大类', colKey: 'categoryId' },
{ title: '字典名称', colKey: 'dictName' },
{ title: '字典编码', colKey: 'dictCode' },
{ title: '字典说明', colKey: 'remark' },
{
......@@ -236,8 +237,12 @@ const handelStatus = (row: ResDictionary, status: number) => {
})
.then(() => {
getData()
MessagePlugin.success('操作成功')
dialog.hide()
})
.catch(() => {
MessagePlugin.error('操作失败')
})
.finally(() => {
dialog.update({ confirmBtn: { content: '确定', loading: false } })
})
......@@ -258,8 +263,12 @@ const handelDel = (row: ResDictionary) => {
})
.then(() => {
getData()
MessagePlugin.success('操作成功')
dialog.hide()
})
.catch(() => {
MessagePlugin.error('操作失败')
})
.finally(() => {
dialog.update({ confirmBtn: { content: '确定删除', loading: false } })
})
......
......@@ -2,7 +2,7 @@
* @Author: xiejiang
* @Date: 2024-11-11 09:38:40
* @LastEditors: xiejiang
* @LastEditTime: 2024-12-03 11:44:08
* @LastEditTime: 2024-12-08 14:54:41
* @Description: 系统字典-新增编辑字典
* Copyright(c)2024 by 好老师教育科技有限公司 All right Reserved.
-->
......
......@@ -2,7 +2,7 @@
* @Author: xiejiang
* @Date: 2024-11-11 09:38:40
* @LastEditors: xiejiang
* @LastEditTime: 2024-12-05 14:03:49
* @LastEditTime: 2024-12-08 15:05:16
* @Description: 菜单管理
* Copyright(c)2024 by 好老师教育科技有限公司 All right Reserved.
-->
......@@ -77,7 +77,7 @@
</template>
<script setup lang="tsx" name="sys-menu">
import { PrimaryTableCol } from 'tdesign-vue-next'
import { PrimaryTableCol, MessagePlugin } from 'tdesign-vue-next'
import type { filterItemProp } from 'src/components/PageFilters/interface/index'
import type { Pagination } from 'src/components/PageContainer/interface/index'
import { useBasic } from '@/hooks/useBasic'
......@@ -104,7 +104,7 @@ const onTabChange = () => {
onMounted(() => {
setBreadcrumb([
{
title: '系统权限'
title: '系统配置'
},
{
title: '菜单管理'
......@@ -187,12 +187,16 @@ const handleStatus = (row: ResMenu, status: number) => {
// 请求成功后,销毁弹框
dialog.update({ confirmBtn: { content: '确定', loading: true } })
submit({
menuId: row?.menuId as number
menuId: row?.menuId as string
})
.then(() => {
MessagePlugin.success('操作成功')
getData()
dialog.hide()
})
.catch(() => {
MessagePlugin.error('操作失败')
})
.finally(() => {
dialog.update({ confirmBtn: { content: '确定', loading: false } })
})
......@@ -207,7 +211,7 @@ const handleDel = (row: ResMenu) => {
// 请求成功后,销毁弹框
dialog.update({ confirmBtn: { content: '确定', loading: true } })
postMenuRemove({
menuId: row?.menuId as number
menuId: row?.menuId as string
})
.then(() => {
getData()
......
......@@ -151,6 +151,7 @@ const open = (val: ResMenu | null) => {
for (const key in formData) {
if (Object.prototype.hasOwnProperty.call(val, key)) {
;(formData as any)[key] = val[key] // 使用类型断言
console.log(formData.isButton)
}
}
getLevelMenu()
......
<template>
<Drawer :drawer-visible="drawerVisible" width="480" title="操作日志" :show-footer="false" @close="close">
<Drawer :drawer-visible="drawerVisible" width="480" title="操作日志" :footer="false" @close="close">
<SearchWithButton
:loading="loading"
placeholder="操作人姓名"
......@@ -19,6 +19,11 @@
<template v-for="(_, name) in $slots" #[name]="scope">
<slot :name="name" v-bind="scope" />
</template>
<template v-for="col in columns" #[col.colKey]="{ row }" :key="col.colKey">
<template v-if="col.colKey != 'operation'">
<div class="cell-content">{{ row[col.colKey] || '-' }}</div>
</template>
</template>
</t-table>
</Drawer>
</template>
......@@ -34,15 +39,17 @@ const drawerVisible = ref(false)
const condInputUserName = ref('')
const loading = ref(false)
const tableData = ref<ResMenu[]>([])
const pagination = ref({
page: 1,
limit: 10,
const pagination = ref<PaginationProps>({
current: 1,
pageSize: 10,
size: 'small',
total: 0
})
// 表格columns配置
const columns: PrimaryTableCol[] = [
{ title: '操作人', colKey: 'inputUserName', ellipsis: true },
{ title: '操作时间', colKey: 'inputTime', ellipsis: true },
{ title: '操作人', colKey: 'inputUserName' },
{ title: '操作时间', colKey: 'inputTime' },
{
title: '操作内容',
colKey: 'content',
......@@ -62,7 +69,8 @@ const columns: PrimaryTableCol[] = [
const getData = () => {
getOperations({
...pagination.value,
page: pagination.value.current as number,
limit: pagination.value.pageSize as number,
condInputUserName: condInputUserName.value
})
.then(res => {
......@@ -81,8 +89,8 @@ const operateTypeTxt = (key: number) => {
// 表格分页
const onPageChange = (val: PaginationProps) => {
pagination.value.page = val.current as number
pagination.value.limit = val.pageSize as number
pagination.value.current = val.current as number
pagination.value.pageSize = val.pageSize as number
getData()
}
......@@ -91,13 +99,13 @@ const open = () => {
getData()
}
const close = () => {
pagination.value.page = 1
pagination.value.limit = 10
pagination.value.current = 1
pagination.value.pageSize = 10
drawerVisible.value = false
}
const handleSearch = () => {
pagination.value.page = 1
pagination.value.current = 1
getData()
}
......
......@@ -2,7 +2,7 @@
* @Author: xiejiang
* @Date: 2024-12-04 14:06:18
* @LastEditors: xiejiang
* @LastEditTime: 2024-12-05 15:25:28
* @LastEditTime: 2024-12-08 10:39:51
* @Description: 菜单排序
* Copyright(c)2024 by 好老师教育科技有限公司 All right Reserved.
-->
......@@ -29,7 +29,7 @@
</template>
<script setup lang="ts" name="menu-sort">
import { TypDragEventState, TreeProps } from 'tdesign-vue-next'
import { TreeProps } from 'tdesign-vue-next'
import { ResMenuTree } from '@/api/interface/menu/index'
import { getMenuTree, postMenuSort } from '@/api/modules/menu/index'
......@@ -84,13 +84,16 @@ function findChildrenByParentId(tree: ResMenuTree[], parentId: string): ResMenuT
return result
}
const handleAllowDrop = (options: TypDragEventState) => {
const { dropPosition } = options
console.log(dropPosition)
const handleAllowDrop: TreeProps['allowDrop'] = ctx => {
const { dropPosition, dropNode, dragNode } = ctx
// 只允许同级拖动
if (dropPosition === 1) {
return true
// 允许同级拖动
if (dragNode?.data?.parentId === dropNode?.data?.parentId) {
return true
} else {
return false
}
} else {
return false
}
......
......@@ -65,7 +65,7 @@
</template>
<script setup lang="tsx" name="version">
import { PrimaryTableCol } from 'tdesign-vue-next'
import { PrimaryTableCol, MessagePlugin } from 'tdesign-vue-next'
import type { filterItemProp } from 'src/components/PageFilters/interface/index'
import type { Pagination } from 'src/components/PageContainer/interface/index'
import { useBasic } from '@/hooks/useBasic'
......@@ -89,7 +89,7 @@ const onTabChange = () => {
onMounted(() => {
setBreadcrumb([
{
title: '系统权限'
title: '系统配置'
},
{
title: '版本管理'
......@@ -178,8 +178,12 @@ const handleStatus = (row: ResVersion, status: number) => {
})
.then(() => {
getData()
MessagePlugin.success('操作成功')
dialog.hide()
})
.catch(() => {
MessagePlugin.error('操作失败')
})
.finally(() => {
dialog.update({ confirmBtn: { content: '确定', loading: false } })
})
......@@ -204,8 +208,12 @@ const handleDel = (row: ResVersion) => {
})
.then(() => {
getData()
MessagePlugin.success('操作成功')
dialog.hide()
})
.catch(() => {
MessagePlugin.error('操作失败')
})
.finally(() => {
dialog.update({ confirmBtn: { content: '确定', loading: false } })
})
......
......@@ -12,6 +12,11 @@
<template v-for="(_, name) in $slots" #[name]="scope">
<slot :name="name" v-bind="scope" />
</template>
<template v-for="col in columns" #[col.colKey]="{ row }" :key="col.colKey">
<template v-if="col.colKey != 'operation'">
<div class="cell-content">{{ row[col.colKey] || '-' }}</div>
</template>
</template>
</t-table>
</Drawer>
</template>
......@@ -32,8 +37,9 @@ const tableData = ref<any[]>([])
// 表格columns配置
const columns: PrimaryTableCol[] = [
{
title: '序号',
colKey: 'serial-number',
width: 50
width: 70
},
{
title: '变更前状态',
......@@ -49,8 +55,8 @@ const columns: PrimaryTableCol[] = [
return <span>{statusVersionTxt(row.afterStatus)?.title}</span>
}
},
{ title: '操作人', colKey: 'inputUserName', ellipsis: true },
{ title: '操作时间', colKey: 'inputTime', ellipsis: true }
{ title: '操作人', colKey: 'inputUserName' },
{ title: '操作时间', colKey: 'inputTime' }
]
const open = (val: ResVersion) => {
......
......@@ -2,7 +2,7 @@
* @Author: xiejiang
* @Date: 2024-11-11 09:38:40
* @LastEditors: xiejiang
* @LastEditTime: 2024-12-06 16:35:08
* @LastEditTime: 2024-12-08 10:28:35
* @Description: 权限配置
* Copyright(c)2024 by 好老师教育科技有限公司 All right Reserved.
-->
......@@ -58,10 +58,17 @@
submitLoading = val
}
"
@updateMenuId="
val => {
sourceMenuId = val
}
"
v-show="tabValue === 2"
/>
</div>
<CopyPermissions ref="copyPermissionsRef" />
<!-- 添加角色 -->
<!-- <Dialog :dialog-visible="dialogVisible" width="480px" title="添加角色" :footer="true" @close="close">
<template #body>
......@@ -95,18 +102,23 @@
</template>
<script setup lang="ts" name="authority">
import { useRoute } from 'vue-router'
import { ResRoleList } from '@/api/interface/user-roles/index'
import { getRoleList } from '@/api/modules/user-roles/index'
import Capabilities from './page/capabilities.vue'
import DataPermissions from './page/data-permissions.vue'
import CopyPermissions from './page/copy-permissions.vue'
import { useBasic } from '@/hooks/useBasic'
const { setBreadcrumb } = useBasic()
// 左边角色相关
const roleName = ref('')
const roleId = ref()
const sourceMenuId = ref('')
const loading = ref(false)
const roles = ref<ResRoleList[]>([])
const route = useRoute()
const handleSearch = () => {
loading.value = true
getRoleList({
......@@ -115,7 +127,12 @@ const handleSearch = () => {
})
.then(res => {
roles.value = res.data
roleId.value = res.data[0]?.roleId
// 区分是否有默认值
if (route.query.roleId) {
roleId.value = route.query.roleId
} else {
roleId.value = res.data[0]?.roleId
}
})
.catch(() => {
roles.value = []
......@@ -164,13 +181,14 @@ const handleSearch = () => {
// }
// 右边相关
const tabValue = ref(2)
const tabValue = ref(1)
const condMenuName = ref('')
const submitLoading = ref(false)
const capabilitiesRef = ref()
const dataPermissionsRef = ref()
const copyPermissionsRef = ref()
const handleSearchTree = () => {
if (tabValue.value == 1) {
capabilitiesRef.value?.getData()
......@@ -189,6 +207,11 @@ const handleConfirm = () => {
const handleClear = () => {
if (tabValue.value == 1) {
capabilitiesRef.value?.clear()
} else {
if (!sourceMenuId.value) {
return MessagePlugin.error('请选择菜单')
}
copyPermissionsRef.value?.open(roleId.value, sourceMenuId.value)
}
}
......
......@@ -2,7 +2,7 @@
* @Author: xiejiang
* @Date: 2024-12-06 17:31:56
* @LastEditors: xiejiang
* @LastEditTime: 2024-12-06 17:39:48
* @LastEditTime: 2024-12-08 10:25:04
* @Description:
* Copyright(c)2024 by 好老师教育科技有限公司 All right Reserved.
-->
......@@ -17,18 +17,33 @@
@confirm="handleConfirm"
@close="close"
>
<t-tree :data="menuTree" activable expand-mutex hover transition> </t-tree>
<t-tree
:data="menuTree"
:checkable="true"
v-model="allChecked"
expand-all
value-mode="all"
activable
hover
transition
>
</t-tree>
</Drawer>
</template>
<script setup lang="ts">
import { MessagePlugin } from 'tdesign-vue-next'
import { ResMenuTree } from '@/api/interface/menu/index'
import { getMenuTree } from '@/api/modules/menu/index'
import { getMenuTree, postDataRangeClone } from '@/api/modules/menu/index'
const menuTree = ref<ResMenuTree[]>([])
const drawerVisible = ref(false)
const submitLoading = ref(false)
const loading = ref(false)
const roleId = ref('')
const sourceMenuId = ref('')
const allChecked = ref([])
// 为树形结构的每个节点添加新参数
function newParamToTree(tree: ResMenuTree[]): ResMenuTree[] {
......@@ -57,10 +72,30 @@ const getData = () => {
})
}
// 保存权限
const handleConfirm = () => {
if (allChecked.value.length == 0) {
MessagePlugin.error('请选择需要克隆的权限!')
return
}
submitLoading.value = true
postDataRangeClone({
roleId: roleId.value,
targetMenuId: allChecked.value,
sourceMenuId: sourceMenuId.value
}).finally(() => {
submitLoading.value = false
close()
})
}
const open = (roleID: string, sourceMenuID: string) => {
roleId.value = roleID
sourceMenuId.value = sourceMenuID
drawerVisible.value = true
}
// 关闭抽屉
const close = () => {
drawerVisible.value = false
}
......@@ -68,6 +103,10 @@ const close = () => {
onMounted(() => {
getData()
})
defineExpose({
open
})
</script>
<style lang="less" scoped></style>
......@@ -103,6 +103,12 @@ watch(toRef(props, 'tabValue'), (val: number) => {
}
})
watch(menuId, () => {
console.log(995)
emit('updateMenuId', menuId.value)
})
// 为树形结构的每个节点添加新参数且删除没有选择数据
function newParamToTree(tree: ResMenuTree[]): ResMenuTree[] {
return tree
......@@ -222,7 +228,7 @@ onMounted(() => {
GetRoleList()
})
const emit = defineEmits(['loading'])
const emit = defineEmits(['loading', 'updateMenuId'])
defineExpose({
getData,
submit
......
......@@ -2,7 +2,7 @@
* @Author: xiejiang
* @Date: 2024-12-01 10:37:37
* @LastEditors: xiejiang
* @LastEditTime: 2024-12-08 09:13:14
* @LastEditTime: 2024-12-08 15:11:47
* @Description:
* Copyright(c)2024 by 好老师教育科技有限公司 All right Reserved.
-->
......@@ -11,7 +11,7 @@
<Back />
<main class="m-auto max-w-[1012px]">
<!-- 基本信息 -->
<PageTitle title="基本信息" class="mb-[24px]" />
<PageTitle title="基本信息" class="mb-[24px] mt-[12px]" />
<div class="flex flex-wrap">
<DetailTable label="角色名称" :value="state?.roleName" />
<DetailTable label="状态">
......@@ -36,6 +36,11 @@
<template v-for="(_, name) in $slots" #[name]="scope">
<slot :name="name" v-bind="scope" />
</template>
<template v-for="col in columns" #[col.colKey]="{ row }" :key="col.colKey">
<template v-if="col.colKey != 'operation'">
<div class="cell-content">{{ row[col.colKey] || '-' }}</div>
</template>
</template>
</t-table>
</main>
</div>
......@@ -78,10 +83,11 @@ const pagination = ref({
// 表格columns配置
const columns: PrimaryTableCol[] = [
{
title: '序号',
colKey: 'serial-number',
width: 50
width: 70
},
{ title: '用户姓名', colKey: 'userName', ellipsis: true },
{ title: '用户姓名', colKey: 'userName' },
{ title: '手机号码', colKey: 'mobile' },
{ title: '最近登录时间', colKey: 'role' },
{ title: '最近登录IP', colKey: 'role' },
......
......@@ -2,7 +2,7 @@
* @Author: xiejiang
* @Date: 2024-11-28 15:01:02
* @LastEditors: xiejiang
* @LastEditTime: 2024-12-05 15:03:23
* @LastEditTime: 2024-12-08 14:05:30
* @Description: 角色管理
* Copyright(c)2024 by 好老师教育科技有限公司 All right Reserved.
-->
......@@ -49,7 +49,7 @@
<script setup lang="tsx" name="users">
import { useRouter } from 'vue-router'
import { PrimaryTableCol } from 'tdesign-vue-next'
import { PrimaryTableCol, MessagePlugin } from 'tdesign-vue-next'
import type { filterItemProp } from '@/components/PageFilters/interface/index'
import type { Pagination } from '@/components/PageContainer/interface/index'
import { statusTxt } from '@/utils/status'
......@@ -104,10 +104,11 @@ const filterConditions = ref<filterItemProp[]>([
// 表格columns配置
const columns: PrimaryTableCol[] = [
{
title: '序号',
colKey: 'serial-number',
width: 50
width: 70
},
{ title: '角色名称', colKey: 'roleName', ellipsis: true },
{ title: '角色名称', colKey: 'roleName' },
{ title: '角色描述', colKey: 'description' },
{
title: '角色状态',
......@@ -188,8 +189,12 @@ const handelStatus = (row: ResRoleList, status: number) => {
})
.then(() => {
getData()
MessagePlugin.success('操作成功')
dialog.hide()
})
.catch(() => {
MessagePlugin.error('操作失败')
})
.finally(() => {
dialog.update({ confirmBtn: { content: '确定', loading: false } })
})
......@@ -209,8 +214,12 @@ const handelDel = (row: ResRoleList) => {
})
.then(() => {
getData()
MessagePlugin.success('操作成功')
dialog.hide()
})
.catch(() => {
MessagePlugin.error('操作失败')
})
.finally(() => {
dialog.update({ confirmBtn: { content: '确定', loading: false } })
})
......@@ -219,7 +228,12 @@ const handelDel = (row: ResRoleList) => {
}
// 权限
const handelPermissions = (row: any) => {
console.log('权限配置', row)
const handelPermissions = (row: ResRoleList) => {
router.push({
name: 'authority',
query: {
roleId: row.roleId
}
})
}
</script>
......@@ -3,7 +3,7 @@
<Back />
<main class="m-auto max-w-[1012px]">
<!-- 基本信息 -->
<PageTitle title="基本信息" class="my-[24px]" />
<PageTitle title="基本信息" class="mb-[24px] mt-[12px]" />
<div class="flex flex-wrap">
<DetailTable :label="'用户名'" :value="detailData?.userName" />
<DetailTable :label="'手机号码'" :value="detailData?.mobile" />
......@@ -31,6 +31,11 @@
<template v-for="(_, name) in $slots" #[name]="scope">
<slot :name="name" v-bind="scope" />
</template>
<template v-for="col in columns" #[col.colKey]="{ row }" :key="col.colKey">
<template v-if="col.colKey != 'operation'">
<div class="cell-content">{{ row[col.colKey] || '-' }}</div>
</template>
</template>
</t-table>
<!-- 登录日志 -->
......@@ -47,6 +52,11 @@
<template v-for="(_, name) in $slots" #[name]="scope">
<slot :name="name" v-bind="scope" />
</template>
<template v-for="col in columns" #[col.colKey]="{ row }" :key="col.colKey">
<template v-if="col.colKey != 'operation'">
<div class="cell-content">{{ row[col.colKey] || '-' }}</div>
</template>
</template>
</t-table>
</main>
</div>
......@@ -79,8 +89,8 @@ const getData = () => {
tableData.value = list.map(
(e: {
menuPermission: {
menuName: any
buttonNames: any[]
menuName: string
buttonNames: string[]
areaPermissions: { areaName: string }[]
rolePermissions: { roleName: string }[]
}[]
......@@ -90,8 +100,8 @@ const getData = () => {
let dataPermission: string[] = []
e.menuPermission.map(
(em: {
menuName: any
buttonNames: any[]
menuName: string
buttonNames: string[]
areaPermissions: { areaName: string }[]
rolePermissions: { roleName: string }[]
}) => {
......@@ -143,12 +153,13 @@ const pagination = ref({
// 表格columns配置
const columns: PrimaryTableCol[] = [
{
title: '序号',
colKey: 'serial-number',
width: 50
width: 70
},
{ title: '角色名称', colKey: 'userName', ellipsis: true },
{ title: '功能权限', colKey: 'functionPermission', ellipsis: true },
{ title: '数据权限', colKey: 'dataPermission', ellipsis: true }
{ title: '角色名称', colKey: 'userName' },
{ title: '功能权限', colKey: 'functionPermission' },
{ title: '数据权限', colKey: 'dataPermission' }
]
// 日志
......@@ -160,8 +171,9 @@ const paginationLog = ref({
})
const columnsLog: PrimaryTableCol[] = [
{
title: '序号',
colKey: 'serial-number',
width: 50
width: 70
},
{ title: '登录时间', colKey: 'loginTime' },
{ title: '登录地点', colKey: 'loginAddress' },
......
......@@ -52,7 +52,7 @@
<script setup lang="tsx" name="users">
import { useRouter } from 'vue-router'
import { PrimaryTableCol } from 'tdesign-vue-next'
import { PrimaryTableCol, MessagePlugin } from 'tdesign-vue-next'
import type { filterItemProp } from 'src/components/PageFilters/interface/index'
import type { Pagination } from 'src/components/PageContainer/interface/index'
import { statusTxt } from '@/utils/status'
......@@ -106,10 +106,11 @@ const filterConditions = computed<filterItemProp[]>(() => [
// 表格columns配置
const columns: PrimaryTableCol[] = [
{
title: '序号',
colKey: 'serial-number',
width: 50
width: 70
},
{ title: '用户姓名', colKey: 'userName', ellipsis: true },
{ title: '用户姓名', colKey: 'userName' },
{ title: '手机号码', colKey: 'mobile' },
{ title: '用户角色', width: 220, colKey: 'roleNames' },
{
......@@ -190,8 +191,12 @@ const handelStatus = (row: ResUserList, status: number) => {
})
.then(() => {
getData()
MessagePlugin.success('操作成功')
dialog.hide()
})
.catch(() => {
MessagePlugin.error('操作失败')
})
.finally(() => {
dialog.update({ confirmBtn: { content: '确定', loading: false } })
})
......@@ -211,8 +216,12 @@ const handelDel = (row: ResUserList) => {
})
.then(() => {
getData()
MessagePlugin.success('操作成功')
dialog.hide()
})
.catch(() => {
MessagePlugin.error('操作失败')
})
.finally(() => {
dialog.update({ confirmBtn: { content: '确定', loading: false } })
})
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论