Compare commits
2
Commits
8657f83c16
...
2908a10ee5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2908a10ee5 | ||
|
|
4433cf521b |
+19
-24
@@ -1,24 +1,19 @@
|
|||||||
import { request } from '../request';
|
import { request } from '../request';
|
||||||
|
|
||||||
/** 获取角色列表
|
/** 获取角色列表
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export function roleListApi() {
|
export function roleListApi() {
|
||||||
return request({ url: '/api/role' });
|
return request({ url: '/api/role' });
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* 登录
|
* 编辑角色
|
||||||
*
|
*
|
||||||
* @param username User name
|
*/
|
||||||
* @param password Password
|
export function editRoleApi(params: { id: number, intro: string, name: string, perms: string[] }) {
|
||||||
*/
|
return request({
|
||||||
export function fetchLogin(username: string, password: string) {
|
url: '/api/role',
|
||||||
return request({
|
method: 'post',
|
||||||
url: '/api/auth/login',
|
data: params
|
||||||
method: 'post',
|
});
|
||||||
data: {
|
}
|
||||||
username,
|
|
||||||
password
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const props = defineProps<{ status: any }>();
|
||||||
|
const BIG_STAGES = ["申请审批", "实施与关闭", "变更关闭"];
|
||||||
|
const bigStageOf = (s: any) => (s === "审批中" ? 0 : s === "已关闭" ? 2 : 1);
|
||||||
|
const cur = bigStageOf(props.status);
|
||||||
|
const pct = ((cur + 1) / BIG_STAGES.length) * 100;
|
||||||
|
const size = 34, stroke = 4, r = (size - stroke) / 2, c = 2 * Math.PI * r;
|
||||||
|
const title = `阶段进度 ${cur + 1}/${BIG_STAGES.length}:${BIG_STAGES.slice(0, cur + 1).join(" → ")}${cur < BIG_STAGES.length - 1 ? " → …" : ""}`;
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<span class="relative inline-flex shrink-0 items-center justify-center" :style="{ width: size + 'px', height: size + 'px' }" :title="title">
|
||||||
|
<svg :width="size" :height="size" class="-rotate-90">
|
||||||
|
<circle :cx="size / 2" :cy="size / 2" :r="r" stroke="#E2E8F0" :stroke-width="stroke" fill="none" />
|
||||||
|
<circle :cx="size / 2" :cy="size / 2" :r="r" stroke="#2080f0" :stroke-width="stroke" fill="none"
|
||||||
|
:stroke-dasharray="c" :stroke-dashoffset="c * (1 - pct / 100)" stroke-linecap="round" />
|
||||||
|
</svg>
|
||||||
|
<span class="absolute inset-0 flex items-center justify-center text-[9px] font-bold text-[#2080f0] dark:text-white">{{ cur + 1 }}/3</span>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
+1648
-1648
File diff suppressed because it is too large
Load Diff
@@ -1,256 +1,212 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, h, onMounted } from 'vue';
|
import { ref, computed, h, onMounted } from 'vue';
|
||||||
import { useAppStore } from '@/store/modules/app';
|
import { useAppStore } from '@/store/modules/app';
|
||||||
import { Icon } from '@iconify/vue'
|
import { Icon } from '@iconify/vue'
|
||||||
import { useThemeStore } from '@/store/modules/theme';
|
import { useThemeStore } from '@/store/modules/theme';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { NButton, NPopconfirm } from 'naive-ui';
|
import { NButton } from 'naive-ui';
|
||||||
import { roleListApi } from '@/service/api/role';
|
import { roleListApi, editRoleApi } from '@/service/api/role';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const themeStore = useThemeStore();
|
const themeStore = useThemeStore();
|
||||||
const gap = computed(() => (appStore.isMobile ? 0 : 16));
|
const gap = computed(() => (appStore.isMobile ? 0 : 16));
|
||||||
|
|
||||||
const loading = ref<boolean>(false);
|
const loading = ref<boolean>(false);
|
||||||
const btnLoading = ref<boolean>(false);
|
const btnLoading = ref<boolean>(false);
|
||||||
const currentType = ref<string>('add');
|
const formRef = ref<any>(null);
|
||||||
const formRef = ref<any>(null);
|
const currentRole = ref<{ id: number, intro: string, name: string, perms: string[] }>({
|
||||||
const currentRole = ref<{ id: number, name: string }>({
|
id: 0,
|
||||||
id: 0,
|
intro: '',
|
||||||
name: '',
|
name: '',
|
||||||
});
|
perms: [],
|
||||||
const rules = ref({
|
});
|
||||||
name: { required: true, message: '角色名称不能为空', trigger: ['blur'] }
|
const rules = ref({
|
||||||
})
|
name: { required: true, message: '角色名称不能为空', trigger: ['blur'] }
|
||||||
const roleModal = ref<boolean>(false);
|
})
|
||||||
const permissionModal = ref<boolean>(false);
|
const roleModal = ref<boolean>(false);
|
||||||
const pagination = ref<{ page: number, pageSize: number, pageCount: number }>({
|
const permissionModal = ref<boolean>(false);
|
||||||
page: 1,
|
const pagination = ref<{ page: number, pageSize: number, pageCount: number }>({
|
||||||
pageSize: 20,
|
page: 1,
|
||||||
pageCount: 0,
|
pageSize: 20,
|
||||||
})
|
pageCount: 0,
|
||||||
const columns = ref([
|
})
|
||||||
{
|
const columns = ref([
|
||||||
title: '序号',
|
{
|
||||||
key: 'index',
|
title: '序号',
|
||||||
width: 5,
|
key: 'index',
|
||||||
align: 'center',
|
width: 5,
|
||||||
render: (_row: any, index: number) => {
|
align: 'center',
|
||||||
return h('span', {}, { default: () => (pagination.value.page - 1) * pagination.value.pageSize + index + 1 })
|
render: (_row: any, index: number) => {
|
||||||
}
|
return h('span', {}, { default: () => (pagination.value.page - 1) * pagination.value.pageSize + index + 1 })
|
||||||
},
|
}
|
||||||
{
|
},
|
||||||
title: '角色名称',
|
{
|
||||||
key: 'name',
|
title: '角色名称',
|
||||||
width: 44,
|
key: 'name',
|
||||||
align: 'center',
|
width: 44,
|
||||||
},
|
align: 'center',
|
||||||
{
|
},
|
||||||
title: '操作',
|
{
|
||||||
key: 'action',
|
title: '操作',
|
||||||
width: 8,
|
key: 'action',
|
||||||
align: 'center',
|
width: 8,
|
||||||
render: (row: any) => {
|
align: 'center',
|
||||||
return h('div', { class: 'flex items-center justify-center gap-3' }, {
|
render: (row: any) => {
|
||||||
default: () => [
|
return h('div', { class: 'flex items-center justify-center gap-3' }, {
|
||||||
h(NButton, {
|
default: () => [
|
||||||
text: true,
|
h(NButton, {
|
||||||
size: 'tiny',
|
text: true,
|
||||||
type: 'primary',
|
size: 'tiny',
|
||||||
onClick: () => handleSetPermission(row)
|
type: 'primary',
|
||||||
}, {
|
onClick: () => handleSetPermission(row)
|
||||||
// 渲染按钮文本
|
}, {
|
||||||
default: () => '设置权限'
|
// 渲染按钮文本
|
||||||
}),
|
default: () => '设置权限'
|
||||||
h(NButton, {
|
}),
|
||||||
text: true,
|
h(NButton, {
|
||||||
size: 'tiny',
|
text: true,
|
||||||
type: 'primary',
|
size: 'tiny',
|
||||||
onClick: () => handleEdit(row)
|
type: 'primary',
|
||||||
}, {
|
onClick: () => handleEdit(row)
|
||||||
// 渲染按钮文本
|
}, {
|
||||||
default: () => '编辑'
|
// 渲染按钮文本
|
||||||
}),
|
default: () => '编辑'
|
||||||
h(NPopconfirm, {
|
}),
|
||||||
// 组件 Props
|
]
|
||||||
positiveText: '确定',
|
})
|
||||||
negativeText: '取消',
|
}
|
||||||
onPositiveClick: () => {
|
},
|
||||||
handleDelete(row)
|
])
|
||||||
},
|
const list = ref<{ id: number, name: string, intro: string, perms: string[] }[]>([])
|
||||||
// 可选:自定义确认/取消按钮的样式
|
|
||||||
positiveButtonProps: { size: 'tiny', type: 'primary' },
|
// 获取列表
|
||||||
negativeButtonProps: { size: 'tiny' }
|
const getList = async () => {
|
||||||
}, {
|
loading.value = true;
|
||||||
// 插槽 (Slots)
|
try {
|
||||||
trigger: () => h(NButton,
|
const res = await roleListApi();
|
||||||
{
|
list.value = res.data;
|
||||||
text: true,
|
} finally {
|
||||||
size: 'tiny',
|
loading.value = false;
|
||||||
type: 'error',
|
}
|
||||||
},
|
}
|
||||||
{
|
|
||||||
default: () => '删除' // 按钮文字
|
// 设置权限
|
||||||
}
|
const handleSetPermission = (row: any) => {
|
||||||
),
|
currentRole.value = JSON.parse(JSON.stringify(row));
|
||||||
default: () => '确定要删除吗?' // 弹出框的提示内容
|
permissionModal.value = true;
|
||||||
}),
|
}
|
||||||
]
|
// 编辑角色
|
||||||
})
|
const handleEdit = (row: any) => {
|
||||||
}
|
currentRole.value = JSON.parse(JSON.stringify(row));
|
||||||
},
|
roleModal.value = true;
|
||||||
])
|
}
|
||||||
const list = ref<{ id: number, name: string, intro: string, perms: string[] }[]>([])
|
// 保存角色
|
||||||
|
const saveRole = (e: MouseEvent) => {
|
||||||
// 获取列表
|
e.preventDefault()
|
||||||
const getList = async () => {
|
formRef.value?.validate(async (errors: any) => {
|
||||||
loading.value = true;
|
if (!errors) {
|
||||||
try {
|
btnLoading.value = true;
|
||||||
const res = await roleListApi();
|
try {
|
||||||
list.value = res.data;
|
await editRoleApi(currentRole.value);
|
||||||
} finally {
|
const target = list.value.find((item:any) => item.id === currentRole.value.id);
|
||||||
loading.value = false;
|
if (target) {
|
||||||
}
|
target.name = currentRole.value.name;
|
||||||
}
|
}
|
||||||
|
window.$message?.success('操作成功')
|
||||||
// 打开添加角色弹框
|
btnLoading.value = false;
|
||||||
const addRole = () => {
|
roleModal.value = false;
|
||||||
currentType.value = 'add';
|
} finally {
|
||||||
currentRole.value = {
|
btnLoading.value = false;
|
||||||
id: 0,
|
}
|
||||||
name: '',
|
}
|
||||||
};
|
})
|
||||||
roleModal.value = true;
|
}
|
||||||
}
|
// 保存权限
|
||||||
// 设置权限
|
const savePermission = async () => {
|
||||||
const handleSetPermission = (row: any) => {
|
btnLoading.value = true;
|
||||||
currentRole.value = {
|
setTimeout(() => {
|
||||||
id: row.id,
|
window.$message?.success('操作成功')
|
||||||
name: row.name,
|
btnLoading.value = false;
|
||||||
};
|
permissionModal.value = false;
|
||||||
permissionModal.value = true;
|
}, 1000)
|
||||||
}
|
}
|
||||||
// 编辑角色
|
|
||||||
const handleEdit = (row: any) => {
|
onMounted(() => {
|
||||||
currentType.value = 'edit';
|
const allRoutes = router.getRoutes();
|
||||||
currentRole.value = {
|
getList()
|
||||||
id: row.id,
|
})
|
||||||
name: row.name,
|
</script>
|
||||||
};
|
|
||||||
roleModal.value = true;
|
<template>
|
||||||
}
|
<NSpace vertical :size="16">
|
||||||
// 删除角色
|
<!-- 角色管理 -->
|
||||||
const handleDelete = (row: any) => {
|
<NCard :bordered="false" size="small" :style="{ borderRadius: themeStore.themeRadius + 'px' }">
|
||||||
loading.value = true;
|
<div class="border mb-3 py-2 px-3" :style="{ borderRadius: themeStore.themeRadius + 'px' }">
|
||||||
setTimeout(() => {
|
<NGrid :x-gap="gap" :y-gap="16" responsive="screen" item-responsive>
|
||||||
loading.value = false;
|
<NGi span="24 s:24 m:24">
|
||||||
list.value = list.value.filter((item: any) => item.id !== row.id);
|
<div class="h-100% flex items-center gap-2">
|
||||||
window.$message?.success('删除成功')
|
<Icon icon="lucide:user" class="size-16px" :style="{ color: themeStore.themeColor }" />
|
||||||
}, 1000)
|
<p class="text-[16px] font-600">角色管理</p>
|
||||||
}
|
</div>
|
||||||
// 保存角色
|
</NGi>
|
||||||
const saveRole = async (e: MouseEvent) => {
|
</NGrid>
|
||||||
e.preventDefault()
|
</div>
|
||||||
formRef.value?.validate((errors: any) => {
|
<div class="scroll">
|
||||||
if (!errors) {
|
<NDataTable
|
||||||
btnLoading.value = true;
|
size="small"
|
||||||
setTimeout(() => {
|
:loading="loading"
|
||||||
// 新增
|
:single-line="false"
|
||||||
if(currentType.value === 'add'){
|
:columns="columns"
|
||||||
list.value.push({id:list.value.length+1,name:currentRole.value?.name})
|
:data="list"
|
||||||
}
|
:pagination="pagination"
|
||||||
// 编辑
|
max-height="calc(100vh - 290px)"
|
||||||
if(currentType.value === 'edit'){
|
/>
|
||||||
const target = list.value.find((item:any) => item.id === currentRole.value.id);
|
</div>
|
||||||
if (target) {
|
<!-- 新增或编辑弹框 -->
|
||||||
target.name = currentRole.value?.name;
|
<NModal
|
||||||
}
|
v-model:show="roleModal"
|
||||||
}
|
preset="card"
|
||||||
window.$message?.success('操作成功')
|
title="编辑角色"
|
||||||
btnLoading.value = false;
|
:auto-focus="false"
|
||||||
roleModal.value = false;
|
:style="{ width: '450px', height: 'auto' }"
|
||||||
}, 1000)
|
:segmented="{ content: true, footer: true }"
|
||||||
}
|
>
|
||||||
})
|
<template #default>
|
||||||
}
|
<NForm ref="formRef" :model="currentRole" :rules="rules" label-placement="left" label-width="auto">
|
||||||
|
<NFormItem label="角色名称" path="name">
|
||||||
onMounted(() => {
|
<NInput
|
||||||
const allRoutes = router.getRoutes();
|
v-model:value="currentRole.name"
|
||||||
getList()
|
placeholder="请输入角色名称"
|
||||||
})
|
/>
|
||||||
</script>
|
</NFormItem>
|
||||||
|
</NForm>
|
||||||
<template>
|
</template>
|
||||||
<NSpace vertical :size="16">
|
<template #footer>
|
||||||
<!-- 角色管理 -->
|
<div class="flex justify-end gap-[10px]">
|
||||||
<NCard :bordered="false" size="small" :style="{ borderRadius: themeStore.themeRadius + 'px' }">
|
<NButton @click="roleModal = false">取消</NButton>
|
||||||
<div class="border mb-3 py-2 px-3" :style="{ borderRadius: themeStore.themeRadius + 'px' }">
|
<NButton type="primary" :loading="btnLoading" @click="saveRole">确定</NButton>
|
||||||
<NGrid :x-gap="gap" :y-gap="16" responsive="screen" item-responsive>
|
</div>
|
||||||
<NGi span="24 s:12 m:12">
|
</template>
|
||||||
<div class="h-100% flex items-center gap-2">
|
</NModal>
|
||||||
<Icon icon="lucide:user" class="size-16px" :style="{ color: themeStore.themeColor }" />
|
<!-- 设置权限抽屉 -->
|
||||||
<p class="text-[16px] font-600">角色管理</p>
|
<NDrawer v-model:show="permissionModal" :width="502" placement="right">
|
||||||
</div>
|
<NDrawerContent :title="`设置权限-${currentRole.name}`" closable>
|
||||||
</NGi>
|
<template #footer>
|
||||||
<NGi span="24 s:12 m:12" class="flex items-center justify-end gap-12px">
|
<div class="flex justify-end gap-[10px]">
|
||||||
<NButton type="primary" size="small" @click="addRole">
|
<NButton @click="permissionModal = false">取消</NButton>
|
||||||
<Icon icon="ic:round-plus" class="size-16px" />
|
<NButton type="primary" :loading="btnLoading" @click="savePermission">保存</NButton>
|
||||||
添加角色
|
</div>
|
||||||
</NButton>
|
</template>
|
||||||
</NGi>
|
</NDrawerContent>
|
||||||
</NGrid>
|
</NDrawer>
|
||||||
</div>
|
</NCard>
|
||||||
<div class="scroll">
|
</NSpace>
|
||||||
<NDataTable
|
</template>
|
||||||
size="small"
|
|
||||||
:loading="loading"
|
<style scoped lang="scss">
|
||||||
:single-line="false"
|
.scroll {
|
||||||
:columns="columns"
|
height: calc(100vh - 205px);
|
||||||
:data="list"
|
overflow-y: auto;
|
||||||
:pagination="pagination"
|
}
|
||||||
max-height="calc(100vh - 290px)"
|
</style>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<!-- 新增或编辑弹框 -->
|
|
||||||
<NModal
|
|
||||||
v-model:show="roleModal"
|
|
||||||
preset="card"
|
|
||||||
:title="currentType === 'add' ? '新增角色' : '编辑角色'"
|
|
||||||
:auto-focus="false"
|
|
||||||
:style="{ width: '450px', height: 'auto' }"
|
|
||||||
:segmented="{ content: true, footer: true }"
|
|
||||||
>
|
|
||||||
<template #default>
|
|
||||||
<NForm ref="formRef" :model="currentRole" :rules="rules" label-placement="left" label-width="auto">
|
|
||||||
<NFormItem label="角色名称" path="name">
|
|
||||||
<NInput
|
|
||||||
v-model:value="currentRole.name"
|
|
||||||
placeholder="请输入角色名称"
|
|
||||||
/>
|
|
||||||
</NFormItem>
|
|
||||||
</NForm>
|
|
||||||
</template>
|
|
||||||
<template #footer>
|
|
||||||
<div class="flex justify-end gap-[10px]">
|
|
||||||
<NButton @click="roleModal = false">取消</NButton>
|
|
||||||
<NButton type="primary" :loading="btnLoading" @click="saveRole">确定</NButton>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</NModal>
|
|
||||||
<!-- 设置权限抽屉 -->
|
|
||||||
<NDrawer v-model:show="permissionModal" :width="502" placement="right">
|
|
||||||
<NDrawerContent :title="`设置权限-${currentRole.name}`" closable>
|
|
||||||
|
|
||||||
</NDrawerContent>
|
|
||||||
</NDrawer>
|
|
||||||
</NCard>
|
|
||||||
</NSpace>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.scroll {
|
|
||||||
height: calc(100vh - 205px);
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user