refactor(views): 重构页面组件代码块顺序
- 调整所有页面组件为template→script→style顺序 - 包括登录页面、仪表盘、系统管理、错误页面等 - 重构demo编辑器页面和监控图表组件 - 统一页面组件结构,提升开发体验 - 注:用户管理页面保留原结构待后续处理
This commit is contained in:
parent
a6c2a3cc5b
commit
1a3fd5220c
@ -1,7 +1,3 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
// 简化的图表占位组件
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="chart-placeholder h-200px w-full flex-center">
|
<div class="chart-placeholder h-200px w-full flex-center">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
@ -15,6 +11,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
// 简化的图表占位组件
|
||||||
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.chart-placeholder {
|
.chart-placeholder {
|
||||||
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
||||||
|
|||||||
@ -1,7 +1,3 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
// 简化的图表占位组件
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="chart-placeholder h-200px w-full flex-center">
|
<div class="chart-placeholder h-200px w-full flex-center">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
@ -15,6 +11,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
// 简化的图表占位组件
|
||||||
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.chart-placeholder {
|
.chart-placeholder {
|
||||||
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
||||||
|
|||||||
@ -1,7 +1,3 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
// 简化的图表占位组件
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="chart-placeholder h-200px w-full flex-center">
|
<div class="chart-placeholder h-200px w-full flex-center">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
@ -15,6 +11,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
// 简化的图表占位组件
|
||||||
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.chart-placeholder {
|
.chart-placeholder {
|
||||||
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
||||||
|
|||||||
@ -1,36 +1,3 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import Chart from './components/chart.vue'
|
|
||||||
import Chart2 from './components/chart2.vue'
|
|
||||||
import Chart3 from './components/chart3.vue'
|
|
||||||
|
|
||||||
const tableData = [
|
|
||||||
{
|
|
||||||
id: 0,
|
|
||||||
name: '商品名称1',
|
|
||||||
start: '2022-02-02',
|
|
||||||
end: '2022-02-02',
|
|
||||||
prograss: '100',
|
|
||||||
status: '已完成',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 0,
|
|
||||||
name: '商品名称2',
|
|
||||||
start: '2022-02-02',
|
|
||||||
end: '2022-02-02',
|
|
||||||
prograss: '50',
|
|
||||||
status: '交易中',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 0,
|
|
||||||
name: '商品名称3',
|
|
||||||
start: '2022-02-02',
|
|
||||||
end: '2022-02-02',
|
|
||||||
prograss: '100',
|
|
||||||
status: '已完成',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<n-grid
|
<n-grid
|
||||||
@ -246,4 +213,37 @@ const tableData = [
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import Chart from './components/chart.vue'
|
||||||
|
import Chart2 from './components/chart2.vue'
|
||||||
|
import Chart3 from './components/chart3.vue'
|
||||||
|
|
||||||
|
const tableData = [
|
||||||
|
{
|
||||||
|
id: 0,
|
||||||
|
name: '商品名称1',
|
||||||
|
start: '2022-02-02',
|
||||||
|
end: '2022-02-02',
|
||||||
|
prograss: '100',
|
||||||
|
status: '已完成',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 0,
|
||||||
|
name: '商品名称2',
|
||||||
|
start: '2022-02-02',
|
||||||
|
end: '2022-02-02',
|
||||||
|
prograss: '50',
|
||||||
|
status: '交易中',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 0,
|
||||||
|
name: '商品名称3',
|
||||||
|
start: '2022-02-02',
|
||||||
|
end: '2022-02-02',
|
||||||
|
prograss: '100',
|
||||||
|
status: '已完成',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|||||||
@ -1,7 +1,3 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
const text = ref('# Hello Editor ')
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<n-card title="MarkDown编辑器">
|
<n-card title="MarkDown编辑器">
|
||||||
<n-space vertical :size="12">
|
<n-space vertical :size="12">
|
||||||
@ -11,4 +7,8 @@ const text = ref('# Hello Editor ')
|
||||||
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|||||||
@ -1,15 +1,3 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
const text = ref('')
|
|
||||||
// 模拟 ajax 异步获取内容
|
|
||||||
onMounted(() => {
|
|
||||||
setTimeout(() => {
|
|
||||||
text.value = '<p>模拟 Ajax 异步设置内容</p>'
|
|
||||||
}, 1500)
|
|
||||||
})
|
|
||||||
|
|
||||||
const active = ref(false)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<n-card title="富文本编辑器">
|
<n-card title="富文本编辑器">
|
||||||
<n-space vertical :size="12">
|
<n-space vertical :size="12">
|
||||||
@ -35,4 +23,16 @@ const active = ref(false)
|
|||||||
</n-card>
|
</n-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const text = ref('')
|
||||||
|
// 模拟 ajax 异步获取内容
|
||||||
|
onMounted(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
text.value = '<p>模拟 Ajax 异步设置内容</p>'
|
||||||
|
}, 1500)
|
||||||
|
})
|
||||||
|
|
||||||
|
const active = ref(false)
|
||||||
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ErrorTip type="403" />
|
<ErrorTip type="403" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped></style>
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ErrorTip type="404" />
|
<ErrorTip type="404" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped></style>
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ErrorTip type="500" />
|
<ErrorTip type="500" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped></style>
|
||||||
|
|||||||
@ -1,102 +1,3 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import type { FormInst } from 'naive-ui'
|
|
||||||
import { useAppStore, useAuthStore } from '@/store'
|
|
||||||
import { fetchCaptchaPng } from '@/service/api/auth'
|
|
||||||
import { local } from '@/utils'
|
|
||||||
|
|
||||||
const emit = defineEmits(['update:modelValue'])
|
|
||||||
|
|
||||||
const authStore = useAuthStore()
|
|
||||||
const appStore = useAppStore()
|
|
||||||
|
|
||||||
// 获取主题色
|
|
||||||
const primaryColor = computed(() => appStore.primaryColor)
|
|
||||||
const primaryColorHover = computed(() => appStore.theme.common.primaryColorHover)
|
|
||||||
const primaryColorPressed = computed(() => appStore.theme.common.primaryColorPressed)
|
|
||||||
|
|
||||||
function toOtherForm(type: any) {
|
|
||||||
emit('update:modelValue', type)
|
|
||||||
}
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
|
||||||
const rules = computed(() => {
|
|
||||||
return {
|
|
||||||
account: {
|
|
||||||
required: true,
|
|
||||||
trigger: 'blur',
|
|
||||||
message: t('login.accountRuleTip'),
|
|
||||||
},
|
|
||||||
pwd: {
|
|
||||||
required: true,
|
|
||||||
trigger: 'blur',
|
|
||||||
message: t('login.passwordRuleTip'),
|
|
||||||
},
|
|
||||||
securityCode: {
|
|
||||||
required: true,
|
|
||||||
trigger: 'blur',
|
|
||||||
message: '请输入验证码',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const formValue = ref({
|
|
||||||
account: 'admin',
|
|
||||||
pwd: '123456',
|
|
||||||
securityCode: '',
|
|
||||||
})
|
|
||||||
const isRemember = ref(false)
|
|
||||||
const isLoading = ref(false)
|
|
||||||
|
|
||||||
// 验证码相关
|
|
||||||
const captchaImage = ref('')
|
|
||||||
const captchaKey = ref('')
|
|
||||||
|
|
||||||
// 获取验证码
|
|
||||||
async function getCaptcha() {
|
|
||||||
try {
|
|
||||||
const { isSuccess, data } = await fetchCaptchaPng()
|
|
||||||
if (isSuccess) {
|
|
||||||
captchaImage.value = data.captchaPicture
|
|
||||||
captchaKey.value = data.codeKey
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
console.warn('[获取验证码失败]:', e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const formRef = ref<FormInst | null>(null)
|
|
||||||
function handleLogin() {
|
|
||||||
formRef.value?.validate(async (errors) => {
|
|
||||||
if (errors)
|
|
||||||
return
|
|
||||||
|
|
||||||
isLoading.value = true
|
|
||||||
const { account, pwd, securityCode } = formValue.value
|
|
||||||
|
|
||||||
if (isRemember.value)
|
|
||||||
local.set('loginAccount', { account, pwd })
|
|
||||||
else local.remove('loginAccount')
|
|
||||||
|
|
||||||
await authStore.login(account, pwd, captchaKey.value, securityCode, isRemember.value)
|
|
||||||
isLoading.value = false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
checkUserAccount()
|
|
||||||
getCaptcha()
|
|
||||||
})
|
|
||||||
|
|
||||||
function checkUserAccount() {
|
|
||||||
const loginAccount = local.get('loginAccount')
|
|
||||||
if (!loginAccount)
|
|
||||||
return
|
|
||||||
|
|
||||||
formValue.value = { ...formValue.value, ...loginAccount }
|
|
||||||
isRemember.value = true
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="space-y-6">
|
<div class="space-y-6">
|
||||||
<!-- 头部问候语 -->
|
<!-- 头部问候语 -->
|
||||||
@ -200,6 +101,105 @@ function checkUserAccount() {
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { FormInst } from 'naive-ui'
|
||||||
|
import { useAppStore, useAuthStore } from '@/store'
|
||||||
|
import { fetchCaptchaPng } from '@/service/api/auth'
|
||||||
|
import { local } from '@/utils'
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
|
||||||
|
const authStore = useAuthStore()
|
||||||
|
const appStore = useAppStore()
|
||||||
|
|
||||||
|
// 获取主题色
|
||||||
|
const primaryColor = computed(() => appStore.primaryColor)
|
||||||
|
const primaryColorHover = computed(() => appStore.theme.common.primaryColorHover)
|
||||||
|
const primaryColorPressed = computed(() => appStore.theme.common.primaryColorPressed)
|
||||||
|
|
||||||
|
function toOtherForm(type: any) {
|
||||||
|
emit('update:modelValue', type)
|
||||||
|
}
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
const rules = computed(() => {
|
||||||
|
return {
|
||||||
|
account: {
|
||||||
|
required: true,
|
||||||
|
trigger: 'blur',
|
||||||
|
message: t('login.accountRuleTip'),
|
||||||
|
},
|
||||||
|
pwd: {
|
||||||
|
required: true,
|
||||||
|
trigger: 'blur',
|
||||||
|
message: t('login.passwordRuleTip'),
|
||||||
|
},
|
||||||
|
securityCode: {
|
||||||
|
required: true,
|
||||||
|
trigger: 'blur',
|
||||||
|
message: '请输入验证码',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const formValue = ref({
|
||||||
|
account: 'admin',
|
||||||
|
pwd: '123456',
|
||||||
|
securityCode: '',
|
||||||
|
})
|
||||||
|
const isRemember = ref(false)
|
||||||
|
const isLoading = ref(false)
|
||||||
|
|
||||||
|
// 验证码相关
|
||||||
|
const captchaImage = ref('')
|
||||||
|
const captchaKey = ref('')
|
||||||
|
|
||||||
|
// 获取验证码
|
||||||
|
async function getCaptcha() {
|
||||||
|
try {
|
||||||
|
const { isSuccess, data } = await fetchCaptchaPng()
|
||||||
|
if (isSuccess) {
|
||||||
|
captchaImage.value = data.captchaPicture
|
||||||
|
captchaKey.value = data.codeKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
console.warn('[获取验证码失败]:', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const formRef = ref<FormInst | null>(null)
|
||||||
|
function handleLogin() {
|
||||||
|
formRef.value?.validate(async (errors) => {
|
||||||
|
if (errors)
|
||||||
|
return
|
||||||
|
|
||||||
|
isLoading.value = true
|
||||||
|
const { account, pwd, securityCode } = formValue.value
|
||||||
|
|
||||||
|
if (isRemember.value)
|
||||||
|
local.set('loginAccount', { account, pwd })
|
||||||
|
else local.remove('loginAccount')
|
||||||
|
|
||||||
|
await authStore.login(account, pwd, captchaKey.value, securityCode, isRemember.value)
|
||||||
|
isLoading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
checkUserAccount()
|
||||||
|
getCaptcha()
|
||||||
|
})
|
||||||
|
|
||||||
|
function checkUserAccount() {
|
||||||
|
const loginAccount = local.get('loginAccount')
|
||||||
|
if (!loginAccount)
|
||||||
|
return
|
||||||
|
|
||||||
|
formValue.value = { ...formValue.value, ...loginAccount }
|
||||||
|
isRemember.value = true
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.login-input {
|
.login-input {
|
||||||
--n-border-radius: 8px;
|
--n-border-radius: 8px;
|
||||||
|
|||||||
@ -1,38 +1,3 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
const emit = defineEmits(['update:modelValue'])
|
|
||||||
function toLogin() {
|
|
||||||
emit('update:modelValue', 'login')
|
|
||||||
}
|
|
||||||
const { t } = useI18n()
|
|
||||||
|
|
||||||
const rules = {
|
|
||||||
account: {
|
|
||||||
required: true,
|
|
||||||
trigger: 'blur',
|
|
||||||
message: t('login.accountRuleTip'),
|
|
||||||
},
|
|
||||||
pwd: {
|
|
||||||
required: true,
|
|
||||||
trigger: 'blur',
|
|
||||||
message: t('login.passwordRuleTip'),
|
|
||||||
},
|
|
||||||
rePwd: {
|
|
||||||
required: true,
|
|
||||||
trigger: 'blur',
|
|
||||||
message: t('login.checkPasswordRuleTip'),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
const formValue = ref({
|
|
||||||
account: 'admin',
|
|
||||||
pwd: '000000',
|
|
||||||
rePwd: '000000',
|
|
||||||
})
|
|
||||||
|
|
||||||
const isRead = ref(false)
|
|
||||||
|
|
||||||
function handleRegister() {}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<n-h2 depth="3" class="text-center">
|
<n-h2 depth="3" class="text-center">
|
||||||
@ -120,4 +85,39 @@ function handleRegister() {}
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
function toLogin() {
|
||||||
|
emit('update:modelValue', 'login')
|
||||||
|
}
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
const rules = {
|
||||||
|
account: {
|
||||||
|
required: true,
|
||||||
|
trigger: 'blur',
|
||||||
|
message: t('login.accountRuleTip'),
|
||||||
|
},
|
||||||
|
pwd: {
|
||||||
|
required: true,
|
||||||
|
trigger: 'blur',
|
||||||
|
message: t('login.passwordRuleTip'),
|
||||||
|
},
|
||||||
|
rePwd: {
|
||||||
|
required: true,
|
||||||
|
trigger: 'blur',
|
||||||
|
message: t('login.checkPasswordRuleTip'),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const formValue = ref({
|
||||||
|
account: 'admin',
|
||||||
|
pwd: '000000',
|
||||||
|
rePwd: '000000',
|
||||||
|
})
|
||||||
|
|
||||||
|
const isRead = ref(false)
|
||||||
|
|
||||||
|
function handleRegister() {}
|
||||||
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|||||||
@ -1,30 +1,3 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import type { FormInst } from 'naive-ui'
|
|
||||||
|
|
||||||
const emit = defineEmits(['update:modelValue'])
|
|
||||||
function toLogin() {
|
|
||||||
emit('update:modelValue', 'login')
|
|
||||||
}
|
|
||||||
const { t } = useI18n()
|
|
||||||
|
|
||||||
const rules = computed(() => {
|
|
||||||
return {
|
|
||||||
account: {
|
|
||||||
required: true,
|
|
||||||
trigger: 'blur',
|
|
||||||
message: t('login.resetPasswordRuleTip'),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const formValue = ref({
|
|
||||||
account: '',
|
|
||||||
})
|
|
||||||
const formRef = ref<FormInst | null>(null)
|
|
||||||
function handleRegister() {
|
|
||||||
formRef.value?.validate()
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<n-h2 depth="3" class="text-center">
|
<n-h2 depth="3" class="text-center">
|
||||||
@ -73,4 +46,31 @@ function handleRegister() {
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { FormInst } from 'naive-ui'
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
function toLogin() {
|
||||||
|
emit('update:modelValue', 'login')
|
||||||
|
}
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
const rules = computed(() => {
|
||||||
|
return {
|
||||||
|
account: {
|
||||||
|
required: true,
|
||||||
|
trigger: 'blur',
|
||||||
|
message: t('login.resetPasswordRuleTip'),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const formValue = ref({
|
||||||
|
account: '',
|
||||||
|
})
|
||||||
|
const formRef = ref<FormInst | null>(null)
|
||||||
|
function handleRegister() {
|
||||||
|
formRef.value?.validate()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|||||||
@ -1,23 +1,3 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { Login, Register, ResetPwd } from './components'
|
|
||||||
import { useAppStore } from '@/store'
|
|
||||||
|
|
||||||
type IformType = 'login' | 'register' | 'resetPwd'
|
|
||||||
const formType: Ref<IformType> = ref('login')
|
|
||||||
const formComponets = {
|
|
||||||
login: Login,
|
|
||||||
register: Register,
|
|
||||||
resetPwd: ResetPwd,
|
|
||||||
}
|
|
||||||
|
|
||||||
const appStore = useAppStore()
|
|
||||||
|
|
||||||
// 获取主题色
|
|
||||||
const primaryColor = computed(() => appStore.primaryColor)
|
|
||||||
const primaryColorHover = computed(() => appStore.theme.common.primaryColorHover)
|
|
||||||
const primaryColorPressed = computed(() => appStore.theme.common.primaryColorPressed)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="h-screen w-screen overflow-hidden">
|
<div class="h-screen w-screen overflow-hidden">
|
||||||
<!-- 全局设置按钮 -->
|
<!-- 全局设置按钮 -->
|
||||||
@ -165,6 +145,26 @@ const primaryColorPressed = computed(() => appStore.theme.common.primaryColorPre
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Login, Register, ResetPwd } from './components'
|
||||||
|
import { useAppStore } from '@/store'
|
||||||
|
|
||||||
|
type IformType = 'login' | 'register' | 'resetPwd'
|
||||||
|
const formType: Ref<IformType> = ref('login')
|
||||||
|
const formComponets = {
|
||||||
|
login: Login,
|
||||||
|
register: Register,
|
||||||
|
resetPwd: ResetPwd,
|
||||||
|
}
|
||||||
|
|
||||||
|
const appStore = useAppStore()
|
||||||
|
|
||||||
|
// 获取主题色
|
||||||
|
const primaryColor = computed(() => appStore.primaryColor)
|
||||||
|
const primaryColorHover = computed(() => appStore.theme.common.primaryColorHover)
|
||||||
|
const primaryColorPressed = computed(() => appStore.theme.common.primaryColorPressed)
|
||||||
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.perspective-1000 {
|
.perspective-1000 {
|
||||||
perspective: 1000px;
|
perspective: 1000px;
|
||||||
|
|||||||
@ -1,3 +1,338 @@
|
|||||||
|
<template>
|
||||||
|
<div class="role-management p-1 bg-gray-50 h-screen flex flex-col">
|
||||||
|
<!-- 搜索表单和角色表格 -->
|
||||||
|
<div class="bg-white rounded-lg shadow-sm border border-gray-100 flex-1 flex flex-col overflow-hidden">
|
||||||
|
<!-- 搜索表单 -->
|
||||||
|
<div class="px-4 py-2 border-b border-gray-100">
|
||||||
|
<n-form
|
||||||
|
ref="searchFormRef"
|
||||||
|
:model="searchForm"
|
||||||
|
label-placement="left"
|
||||||
|
label-width="auto"
|
||||||
|
class="search-form"
|
||||||
|
>
|
||||||
|
<n-grid :cols="6" :x-gap="8" :y-gap="4">
|
||||||
|
<n-grid-item>
|
||||||
|
<n-form-item label="角色名称" path="roleName">
|
||||||
|
<n-input
|
||||||
|
v-model:value="searchForm.roleName"
|
||||||
|
placeholder="请输入角色名称"
|
||||||
|
clearable
|
||||||
|
@keydown.enter="handleSearch"
|
||||||
|
/>
|
||||||
|
</n-form-item>
|
||||||
|
</n-grid-item>
|
||||||
|
|
||||||
|
<n-grid-item>
|
||||||
|
<n-form-item label="角色编码" path="roleCode">
|
||||||
|
<n-input
|
||||||
|
v-model:value="searchForm.roleCode"
|
||||||
|
placeholder="请输入角色编码"
|
||||||
|
clearable
|
||||||
|
@keydown.enter="handleSearch"
|
||||||
|
/>
|
||||||
|
</n-form-item>
|
||||||
|
</n-grid-item>
|
||||||
|
|
||||||
|
<n-grid-item>
|
||||||
|
<n-form-item label="创建时间" path="timeRange">
|
||||||
|
<n-date-picker
|
||||||
|
v-model:value="searchForm.timeRange"
|
||||||
|
type="datetimerange"
|
||||||
|
clearable
|
||||||
|
placeholder="选择时间范围"
|
||||||
|
:shortcuts="{
|
||||||
|
今天: () => [new Date().setHours(0, 0, 0, 0), new Date().setHours(23, 59, 59, 999)],
|
||||||
|
昨天: () => {
|
||||||
|
const yesterday = new Date();
|
||||||
|
yesterday.setDate(yesterday.getDate() - 1);
|
||||||
|
return [yesterday.setHours(0, 0, 0, 0), yesterday.setHours(23, 59, 59, 999)];
|
||||||
|
},
|
||||||
|
最近7天: () => [Date.now() - 7 * 24 * 60 * 60 * 1000, Date.now()],
|
||||||
|
最近30天: () => [Date.now() - 30 * 24 * 60 * 60 * 1000, Date.now()],
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
</n-form-item>
|
||||||
|
</n-grid-item>
|
||||||
|
|
||||||
|
<n-grid-item>
|
||||||
|
<n-form-item label="角色状态" path="roleStatus">
|
||||||
|
<n-select
|
||||||
|
v-model:value="searchForm.roleStatus"
|
||||||
|
placeholder="请选择角色状态"
|
||||||
|
clearable
|
||||||
|
:options="[
|
||||||
|
{ label: '启用', value: '0' },
|
||||||
|
{ label: '停用', value: '1' },
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</n-form-item>
|
||||||
|
</n-grid-item>
|
||||||
|
|
||||||
|
<n-grid-item>
|
||||||
|
<n-form-item label="" path="">
|
||||||
|
<NSpace>
|
||||||
|
<NButton type="primary" @click="handleSearch">
|
||||||
|
<template #icon>
|
||||||
|
<NIcon><icon-park-outline:search /></NIcon>
|
||||||
|
</template>
|
||||||
|
搜索
|
||||||
|
</NButton>
|
||||||
|
<NButton @click="handleReset">
|
||||||
|
<template #icon>
|
||||||
|
<NIcon><icon-park-outline:refresh /></NIcon>
|
||||||
|
</template>
|
||||||
|
重置
|
||||||
|
</NButton>
|
||||||
|
</NSpace>
|
||||||
|
</n-form-item>
|
||||||
|
</n-grid-item>
|
||||||
|
</n-grid>
|
||||||
|
</n-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 表格头部操作栏 -->
|
||||||
|
<div class="flex items-center justify-between px-4 py-2 border-b border-gray-100">
|
||||||
|
<div class="flex items-center gap-4">
|
||||||
|
<NButton type="primary" class="px-6 flex items-center" @click="handleAdd">
|
||||||
|
<template #icon>
|
||||||
|
<NIcon class="mr-1" style="transform: translateY(-1px)">
|
||||||
|
<icon-park-outline:plus />
|
||||||
|
</NIcon>
|
||||||
|
</template>
|
||||||
|
新增
|
||||||
|
</NButton>
|
||||||
|
|
||||||
|
<NButton
|
||||||
|
type="error"
|
||||||
|
:disabled="selectedRows.length === 0"
|
||||||
|
class="px-6 flex items-center"
|
||||||
|
@click="handleBatchDelete"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<NIcon class="mr-1" style="transform: translateY(-1px)">
|
||||||
|
<icon-park-outline:delete />
|
||||||
|
</NIcon>
|
||||||
|
</template>
|
||||||
|
删除
|
||||||
|
</NButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center gap-4 text-sm text-gray-500">
|
||||||
|
<span>共 {{ pagination.itemCount }} 条</span>
|
||||||
|
<NButton text @click="getRoleList">
|
||||||
|
<template #icon>
|
||||||
|
<NIcon><icon-park-outline:refresh /></NIcon>
|
||||||
|
</template>
|
||||||
|
</NButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 表格内容 -->
|
||||||
|
<div class="table-wrapper flex-1 overflow-auto pt-4">
|
||||||
|
<n-data-table
|
||||||
|
:columns="columns"
|
||||||
|
:data="tableData"
|
||||||
|
:loading="loading"
|
||||||
|
:row-key="(row: RoleVo) => row.roleId"
|
||||||
|
:bordered="false"
|
||||||
|
:single-line="false"
|
||||||
|
size="large"
|
||||||
|
class="custom-table"
|
||||||
|
@update:checked-row-keys="handleRowSelectionChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 分页器 -->
|
||||||
|
<div class="flex items-center px-4 py-2 border-t border-gray-100">
|
||||||
|
<div class="text-sm text-gray-500 mr-4">
|
||||||
|
共 {{ pagination.itemCount }} 条
|
||||||
|
</div>
|
||||||
|
<n-pagination
|
||||||
|
v-model:page="pagination.page"
|
||||||
|
v-model:page-size="pagination.pageSize"
|
||||||
|
:item-count="pagination.itemCount"
|
||||||
|
:show-size-picker="true"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
show-quick-jumper
|
||||||
|
@update:page="handlePageChange"
|
||||||
|
@update:page-size="handlePageSizeChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 角色表单弹框 -->
|
||||||
|
<NovaDialog
|
||||||
|
ref="roleDialogRef"
|
||||||
|
:title="modalTitle"
|
||||||
|
:width="700"
|
||||||
|
height="auto"
|
||||||
|
confirm-text="确定"
|
||||||
|
cancel-text="取消"
|
||||||
|
@nova-confirm="handleSubmit"
|
||||||
|
@nova-cancel="handleCancel"
|
||||||
|
>
|
||||||
|
<template #content>
|
||||||
|
<div class="px-3 py-2">
|
||||||
|
<n-form
|
||||||
|
ref="formRef"
|
||||||
|
:model="formData"
|
||||||
|
:rules="rules"
|
||||||
|
label-placement="left"
|
||||||
|
label-width="90px"
|
||||||
|
require-mark-placement="right-hanging"
|
||||||
|
>
|
||||||
|
<!-- 第一行:角色名称 和 角色编码 -->
|
||||||
|
<n-grid :cols="2" :x-gap="12">
|
||||||
|
<n-grid-item>
|
||||||
|
<n-form-item label="角色名称" path="roleName">
|
||||||
|
<n-input
|
||||||
|
v-model:value="formData.roleName"
|
||||||
|
placeholder="请输入角色名称"
|
||||||
|
/>
|
||||||
|
</n-form-item>
|
||||||
|
</n-grid-item>
|
||||||
|
<n-grid-item>
|
||||||
|
<n-form-item label="角色编码" path="roleCode">
|
||||||
|
<n-input
|
||||||
|
v-model:value="formData.roleCode"
|
||||||
|
placeholder="请输入角色编码"
|
||||||
|
:disabled="isEdit"
|
||||||
|
/>
|
||||||
|
</n-form-item>
|
||||||
|
</n-grid-item>
|
||||||
|
</n-grid>
|
||||||
|
|
||||||
|
<!-- 第二行:角色状态 和 显示排序 -->
|
||||||
|
<n-grid :cols="2" :x-gap="12">
|
||||||
|
<n-grid-item>
|
||||||
|
<n-form-item label="角色状态" path="roleStatus">
|
||||||
|
<n-select
|
||||||
|
v-model:value="formData.roleStatus"
|
||||||
|
placeholder="请选择角色状态"
|
||||||
|
:options="[
|
||||||
|
{ label: '启用', value: '0' },
|
||||||
|
{ label: '停用', value: '1' },
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</n-form-item>
|
||||||
|
</n-grid-item>
|
||||||
|
<n-grid-item>
|
||||||
|
<n-form-item label="显示排序" path="sorted">
|
||||||
|
<n-input-number
|
||||||
|
v-model:value="formData.sorted"
|
||||||
|
placeholder="请输入排序号"
|
||||||
|
:min="1"
|
||||||
|
:max="999"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</n-form-item>
|
||||||
|
</n-grid-item>
|
||||||
|
</n-grid>
|
||||||
|
|
||||||
|
<!-- 第三行:角色描述(占满整行) -->
|
||||||
|
<n-form-item label="角色描述" path="remark">
|
||||||
|
<n-input
|
||||||
|
v-model:value="formData.remark"
|
||||||
|
type="textarea"
|
||||||
|
placeholder="请输入角色描述"
|
||||||
|
:rows="3"
|
||||||
|
/>
|
||||||
|
</n-form-item>
|
||||||
|
</n-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</NovaDialog>
|
||||||
|
|
||||||
|
<!-- 菜单权限分配弹框 -->
|
||||||
|
<NovaDialog
|
||||||
|
ref="menuDialogRef"
|
||||||
|
title="分配菜单权限"
|
||||||
|
:width="500"
|
||||||
|
height="auto"
|
||||||
|
confirm-text="确定"
|
||||||
|
cancel-text="取消"
|
||||||
|
@nova-confirm="handleConfirmAssignMenu"
|
||||||
|
@nova-cancel="handleCancelAssignMenu"
|
||||||
|
>
|
||||||
|
<template #content>
|
||||||
|
<div class="p-3">
|
||||||
|
<div v-if="menuLoading" class="flex items-center justify-center py-8">
|
||||||
|
<div class="text-center">
|
||||||
|
<n-spin size="medium" />
|
||||||
|
<p class="mt-2 text-sm text-gray-500">
|
||||||
|
正在加载菜单数据...
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else-if="menuData.length > 0">
|
||||||
|
<!-- 功能按钮区域 -->
|
||||||
|
<div class="flex items-center gap-2 mb-3 pb-3 border-b border-gray-200">
|
||||||
|
<NButton
|
||||||
|
size="small"
|
||||||
|
:type="allExpanded ? 'default' : 'primary'"
|
||||||
|
@click="toggleExpandAll"
|
||||||
|
>
|
||||||
|
{{ allExpanded ? '折叠' : '展开' }}
|
||||||
|
</NButton>
|
||||||
|
|
||||||
|
<NButton
|
||||||
|
size="small"
|
||||||
|
:type="allSelected ? 'default' : 'primary'"
|
||||||
|
@click="toggleSelectAll"
|
||||||
|
>
|
||||||
|
{{ allSelected ? '全不选' : '全选' }}
|
||||||
|
</NButton>
|
||||||
|
|
||||||
|
<NButton
|
||||||
|
size="small"
|
||||||
|
:type="cascadeEnabled ? 'primary' : 'default'"
|
||||||
|
@click="toggleCascade"
|
||||||
|
>
|
||||||
|
父子联动
|
||||||
|
</NButton>
|
||||||
|
|
||||||
|
<NButton
|
||||||
|
size="small"
|
||||||
|
:type="showPermissionCode ? 'primary' : 'default'"
|
||||||
|
@click="togglePermissionCode"
|
||||||
|
>
|
||||||
|
权限标识
|
||||||
|
</NButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<NTree
|
||||||
|
v-model:checked-keys="checkedKeys"
|
||||||
|
v-model:expanded-keys="expandedKeys"
|
||||||
|
:data="menuData"
|
||||||
|
checkable
|
||||||
|
:cascade="cascadeEnabled"
|
||||||
|
check-strategy="all"
|
||||||
|
expand-on-click
|
||||||
|
:render-suffix="showPermissionCode ? renderPermissionCode : undefined"
|
||||||
|
class="clean-menu-tree"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else class="flex items-center justify-center py-8">
|
||||||
|
<div class="text-center">
|
||||||
|
<div class="w-10 h-10 bg-gray-100 rounded-full flex items-center justify-center mx-auto mb-2">
|
||||||
|
<NIcon size="16" class="text-gray-400">
|
||||||
|
<icon-park-outline:folder-close />
|
||||||
|
</NIcon>
|
||||||
|
</div>
|
||||||
|
<p class="text-sm text-gray-500">
|
||||||
|
暂无可分配的菜单
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</NovaDialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { h, nextTick, onMounted, ref, watch } from 'vue'
|
import { h, nextTick, onMounted, ref, watch } from 'vue'
|
||||||
import type { DataTableColumns, FormInst } from 'naive-ui'
|
import type { DataTableColumns, FormInst } from 'naive-ui'
|
||||||
@ -771,341 +1106,6 @@ onMounted(() => {
|
|||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="role-management p-1 bg-gray-50 h-screen flex flex-col">
|
|
||||||
<!-- 搜索表单和角色表格 -->
|
|
||||||
<div class="bg-white rounded-lg shadow-sm border border-gray-100 flex-1 flex flex-col overflow-hidden">
|
|
||||||
<!-- 搜索表单 -->
|
|
||||||
<div class="px-4 py-2 border-b border-gray-100">
|
|
||||||
<n-form
|
|
||||||
ref="searchFormRef"
|
|
||||||
:model="searchForm"
|
|
||||||
label-placement="left"
|
|
||||||
label-width="auto"
|
|
||||||
class="search-form"
|
|
||||||
>
|
|
||||||
<n-grid :cols="6" :x-gap="8" :y-gap="4">
|
|
||||||
<n-grid-item>
|
|
||||||
<n-form-item label="角色名称" path="roleName">
|
|
||||||
<n-input
|
|
||||||
v-model:value="searchForm.roleName"
|
|
||||||
placeholder="请输入角色名称"
|
|
||||||
clearable
|
|
||||||
@keydown.enter="handleSearch"
|
|
||||||
/>
|
|
||||||
</n-form-item>
|
|
||||||
</n-grid-item>
|
|
||||||
|
|
||||||
<n-grid-item>
|
|
||||||
<n-form-item label="角色编码" path="roleCode">
|
|
||||||
<n-input
|
|
||||||
v-model:value="searchForm.roleCode"
|
|
||||||
placeholder="请输入角色编码"
|
|
||||||
clearable
|
|
||||||
@keydown.enter="handleSearch"
|
|
||||||
/>
|
|
||||||
</n-form-item>
|
|
||||||
</n-grid-item>
|
|
||||||
|
|
||||||
<n-grid-item>
|
|
||||||
<n-form-item label="创建时间" path="timeRange">
|
|
||||||
<n-date-picker
|
|
||||||
v-model:value="searchForm.timeRange"
|
|
||||||
type="datetimerange"
|
|
||||||
clearable
|
|
||||||
placeholder="选择时间范围"
|
|
||||||
:shortcuts="{
|
|
||||||
今天: () => [new Date().setHours(0, 0, 0, 0), new Date().setHours(23, 59, 59, 999)],
|
|
||||||
昨天: () => {
|
|
||||||
const yesterday = new Date();
|
|
||||||
yesterday.setDate(yesterday.getDate() - 1);
|
|
||||||
return [yesterday.setHours(0, 0, 0, 0), yesterday.setHours(23, 59, 59, 999)];
|
|
||||||
},
|
|
||||||
最近7天: () => [Date.now() - 7 * 24 * 60 * 60 * 1000, Date.now()],
|
|
||||||
最近30天: () => [Date.now() - 30 * 24 * 60 * 60 * 1000, Date.now()],
|
|
||||||
}"
|
|
||||||
/>
|
|
||||||
</n-form-item>
|
|
||||||
</n-grid-item>
|
|
||||||
|
|
||||||
<n-grid-item>
|
|
||||||
<n-form-item label="角色状态" path="roleStatus">
|
|
||||||
<n-select
|
|
||||||
v-model:value="searchForm.roleStatus"
|
|
||||||
placeholder="请选择角色状态"
|
|
||||||
clearable
|
|
||||||
:options="[
|
|
||||||
{ label: '启用', value: '0' },
|
|
||||||
{ label: '停用', value: '1' },
|
|
||||||
]"
|
|
||||||
/>
|
|
||||||
</n-form-item>
|
|
||||||
</n-grid-item>
|
|
||||||
|
|
||||||
<n-grid-item>
|
|
||||||
<n-form-item label="" path="">
|
|
||||||
<NSpace>
|
|
||||||
<NButton type="primary" @click="handleSearch">
|
|
||||||
<template #icon>
|
|
||||||
<NIcon><icon-park-outline:search /></NIcon>
|
|
||||||
</template>
|
|
||||||
搜索
|
|
||||||
</NButton>
|
|
||||||
<NButton @click="handleReset">
|
|
||||||
<template #icon>
|
|
||||||
<NIcon><icon-park-outline:refresh /></NIcon>
|
|
||||||
</template>
|
|
||||||
重置
|
|
||||||
</NButton>
|
|
||||||
</NSpace>
|
|
||||||
</n-form-item>
|
|
||||||
</n-grid-item>
|
|
||||||
</n-grid>
|
|
||||||
</n-form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 表格头部操作栏 -->
|
|
||||||
<div class="flex items-center justify-between px-4 py-2 border-b border-gray-100">
|
|
||||||
<div class="flex items-center gap-4">
|
|
||||||
<NButton type="primary" class="px-6 flex items-center" @click="handleAdd">
|
|
||||||
<template #icon>
|
|
||||||
<NIcon class="mr-1" style="transform: translateY(-1px)">
|
|
||||||
<icon-park-outline:plus />
|
|
||||||
</NIcon>
|
|
||||||
</template>
|
|
||||||
新增
|
|
||||||
</NButton>
|
|
||||||
|
|
||||||
<NButton
|
|
||||||
type="error"
|
|
||||||
:disabled="selectedRows.length === 0"
|
|
||||||
class="px-6 flex items-center"
|
|
||||||
@click="handleBatchDelete"
|
|
||||||
>
|
|
||||||
<template #icon>
|
|
||||||
<NIcon class="mr-1" style="transform: translateY(-1px)">
|
|
||||||
<icon-park-outline:delete />
|
|
||||||
</NIcon>
|
|
||||||
</template>
|
|
||||||
删除
|
|
||||||
</NButton>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex items-center gap-4 text-sm text-gray-500">
|
|
||||||
<span>共 {{ pagination.itemCount }} 条</span>
|
|
||||||
<NButton text @click="getRoleList">
|
|
||||||
<template #icon>
|
|
||||||
<NIcon><icon-park-outline:refresh /></NIcon>
|
|
||||||
</template>
|
|
||||||
</NButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 表格内容 -->
|
|
||||||
<div class="table-wrapper flex-1 overflow-auto pt-4">
|
|
||||||
<n-data-table
|
|
||||||
:columns="columns"
|
|
||||||
:data="tableData"
|
|
||||||
:loading="loading"
|
|
||||||
:row-key="(row: RoleVo) => row.roleId"
|
|
||||||
:bordered="false"
|
|
||||||
:single-line="false"
|
|
||||||
size="large"
|
|
||||||
class="custom-table"
|
|
||||||
@update:checked-row-keys="handleRowSelectionChange"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 分页器 -->
|
|
||||||
<div class="flex items-center px-4 py-2 border-t border-gray-100">
|
|
||||||
<div class="text-sm text-gray-500 mr-4">
|
|
||||||
共 {{ pagination.itemCount }} 条
|
|
||||||
</div>
|
|
||||||
<n-pagination
|
|
||||||
v-model:page="pagination.page"
|
|
||||||
v-model:page-size="pagination.pageSize"
|
|
||||||
:item-count="pagination.itemCount"
|
|
||||||
:show-size-picker="true"
|
|
||||||
:page-sizes="[10, 20, 50, 100]"
|
|
||||||
show-quick-jumper
|
|
||||||
@update:page="handlePageChange"
|
|
||||||
@update:page-size="handlePageSizeChange"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 角色表单弹框 -->
|
|
||||||
<NovaDialog
|
|
||||||
ref="roleDialogRef"
|
|
||||||
:title="modalTitle"
|
|
||||||
:width="700"
|
|
||||||
height="auto"
|
|
||||||
confirm-text="确定"
|
|
||||||
cancel-text="取消"
|
|
||||||
@nova-confirm="handleSubmit"
|
|
||||||
@nova-cancel="handleCancel"
|
|
||||||
>
|
|
||||||
<template #content>
|
|
||||||
<div class="px-3 py-2">
|
|
||||||
<n-form
|
|
||||||
ref="formRef"
|
|
||||||
:model="formData"
|
|
||||||
:rules="rules"
|
|
||||||
label-placement="left"
|
|
||||||
label-width="90px"
|
|
||||||
require-mark-placement="right-hanging"
|
|
||||||
>
|
|
||||||
<!-- 第一行:角色名称 和 角色编码 -->
|
|
||||||
<n-grid :cols="2" :x-gap="12">
|
|
||||||
<n-grid-item>
|
|
||||||
<n-form-item label="角色名称" path="roleName">
|
|
||||||
<n-input
|
|
||||||
v-model:value="formData.roleName"
|
|
||||||
placeholder="请输入角色名称"
|
|
||||||
/>
|
|
||||||
</n-form-item>
|
|
||||||
</n-grid-item>
|
|
||||||
<n-grid-item>
|
|
||||||
<n-form-item label="角色编码" path="roleCode">
|
|
||||||
<n-input
|
|
||||||
v-model:value="formData.roleCode"
|
|
||||||
placeholder="请输入角色编码"
|
|
||||||
:disabled="isEdit"
|
|
||||||
/>
|
|
||||||
</n-form-item>
|
|
||||||
</n-grid-item>
|
|
||||||
</n-grid>
|
|
||||||
|
|
||||||
<!-- 第二行:角色状态 和 显示排序 -->
|
|
||||||
<n-grid :cols="2" :x-gap="12">
|
|
||||||
<n-grid-item>
|
|
||||||
<n-form-item label="角色状态" path="roleStatus">
|
|
||||||
<n-select
|
|
||||||
v-model:value="formData.roleStatus"
|
|
||||||
placeholder="请选择角色状态"
|
|
||||||
:options="[
|
|
||||||
{ label: '启用', value: '0' },
|
|
||||||
{ label: '停用', value: '1' },
|
|
||||||
]"
|
|
||||||
/>
|
|
||||||
</n-form-item>
|
|
||||||
</n-grid-item>
|
|
||||||
<n-grid-item>
|
|
||||||
<n-form-item label="显示排序" path="sorted">
|
|
||||||
<n-input-number
|
|
||||||
v-model:value="formData.sorted"
|
|
||||||
placeholder="请输入排序号"
|
|
||||||
:min="1"
|
|
||||||
:max="999"
|
|
||||||
style="width: 100%"
|
|
||||||
/>
|
|
||||||
</n-form-item>
|
|
||||||
</n-grid-item>
|
|
||||||
</n-grid>
|
|
||||||
|
|
||||||
<!-- 第三行:角色描述(占满整行) -->
|
|
||||||
<n-form-item label="角色描述" path="remark">
|
|
||||||
<n-input
|
|
||||||
v-model:value="formData.remark"
|
|
||||||
type="textarea"
|
|
||||||
placeholder="请输入角色描述"
|
|
||||||
:rows="3"
|
|
||||||
/>
|
|
||||||
</n-form-item>
|
|
||||||
</n-form>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</NovaDialog>
|
|
||||||
|
|
||||||
<!-- 菜单权限分配弹框 -->
|
|
||||||
<NovaDialog
|
|
||||||
ref="menuDialogRef"
|
|
||||||
title="分配菜单权限"
|
|
||||||
:width="500"
|
|
||||||
height="auto"
|
|
||||||
confirm-text="确定"
|
|
||||||
cancel-text="取消"
|
|
||||||
@nova-confirm="handleConfirmAssignMenu"
|
|
||||||
@nova-cancel="handleCancelAssignMenu"
|
|
||||||
>
|
|
||||||
<template #content>
|
|
||||||
<div class="p-3">
|
|
||||||
<div v-if="menuLoading" class="flex items-center justify-center py-8">
|
|
||||||
<div class="text-center">
|
|
||||||
<n-spin size="medium" />
|
|
||||||
<p class="mt-2 text-sm text-gray-500">
|
|
||||||
正在加载菜单数据...
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-else-if="menuData.length > 0">
|
|
||||||
<!-- 功能按钮区域 -->
|
|
||||||
<div class="flex items-center gap-2 mb-3 pb-3 border-b border-gray-200">
|
|
||||||
<NButton
|
|
||||||
size="small"
|
|
||||||
:type="allExpanded ? 'default' : 'primary'"
|
|
||||||
@click="toggleExpandAll"
|
|
||||||
>
|
|
||||||
{{ allExpanded ? '折叠' : '展开' }}
|
|
||||||
</NButton>
|
|
||||||
|
|
||||||
<NButton
|
|
||||||
size="small"
|
|
||||||
:type="allSelected ? 'default' : 'primary'"
|
|
||||||
@click="toggleSelectAll"
|
|
||||||
>
|
|
||||||
{{ allSelected ? '全不选' : '全选' }}
|
|
||||||
</NButton>
|
|
||||||
|
|
||||||
<NButton
|
|
||||||
size="small"
|
|
||||||
:type="cascadeEnabled ? 'primary' : 'default'"
|
|
||||||
@click="toggleCascade"
|
|
||||||
>
|
|
||||||
父子联动
|
|
||||||
</NButton>
|
|
||||||
|
|
||||||
<NButton
|
|
||||||
size="small"
|
|
||||||
:type="showPermissionCode ? 'primary' : 'default'"
|
|
||||||
@click="togglePermissionCode"
|
|
||||||
>
|
|
||||||
权限标识
|
|
||||||
</NButton>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<NTree
|
|
||||||
v-model:checked-keys="checkedKeys"
|
|
||||||
v-model:expanded-keys="expandedKeys"
|
|
||||||
:data="menuData"
|
|
||||||
checkable
|
|
||||||
:cascade="cascadeEnabled"
|
|
||||||
check-strategy="all"
|
|
||||||
expand-on-click
|
|
||||||
:render-suffix="showPermissionCode ? renderPermissionCode : undefined"
|
|
||||||
class="clean-menu-tree"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-else class="flex items-center justify-center py-8">
|
|
||||||
<div class="text-center">
|
|
||||||
<div class="w-10 h-10 bg-gray-100 rounded-full flex items-center justify-center mx-auto mb-2">
|
|
||||||
<NIcon size="16" class="text-gray-400">
|
|
||||||
<icon-park-outline:folder-close />
|
|
||||||
</NIcon>
|
|
||||||
</div>
|
|
||||||
<p class="text-sm text-gray-500">
|
|
||||||
暂无可分配的菜单
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</NovaDialog>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.role-management {
|
.role-management {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user