feat(view): base view init
parent
4b2ef049f3
commit
58db52cea8
|
@ -0,0 +1,64 @@
|
||||||
|
<template>
|
||||||
|
<CollapseContainer title="账号绑定" :canExpan="false">
|
||||||
|
<List>
|
||||||
|
<template v-for="item in accountBindList" :key="item.key">
|
||||||
|
<ListItem>
|
||||||
|
<ListItemMeta>
|
||||||
|
<template #avatar>
|
||||||
|
<Icon v-if="item.avatar" class="avatar" :icon="item.avatar" :color="item.color" />
|
||||||
|
</template>
|
||||||
|
<template #title>
|
||||||
|
{{ item.title }}
|
||||||
|
<a-button type="link" size="small" v-if="item.extra" class="extra">
|
||||||
|
{{ item.extra }}
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
<template #description>
|
||||||
|
<div>{{ item.description }}</div>
|
||||||
|
</template>
|
||||||
|
</ListItemMeta>
|
||||||
|
</ListItem>
|
||||||
|
</template>
|
||||||
|
</List>
|
||||||
|
</CollapseContainer>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { List } from 'ant-design-vue'
|
||||||
|
import { CollapseContainer } from '@/components/Container/index'
|
||||||
|
import { accountBindList } from './data'
|
||||||
|
import { getUserProfileApi } from '@/api/base/profile'
|
||||||
|
import { onMounted } from 'vue'
|
||||||
|
|
||||||
|
const ListItem = List.Item
|
||||||
|
const ListItemMeta = List.Item.Meta
|
||||||
|
|
||||||
|
async function init() {
|
||||||
|
const userInfo = await getUserProfileApi()
|
||||||
|
// TODO
|
||||||
|
for (const i in accountBindList) {
|
||||||
|
if (userInfo.socialUsers) {
|
||||||
|
for (const j in userInfo.socialUsers) {
|
||||||
|
if (accountBindList[i].key === userInfo.socialUsers[j].type) {
|
||||||
|
accountBindList[i].title = '已綁定'
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onMounted(async () => {
|
||||||
|
await init()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.avatar {
|
||||||
|
font-size: 40px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.extra {
|
||||||
|
float: right;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-right: 30px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,80 @@
|
||||||
|
<template>
|
||||||
|
<CollapseContainer title="基本设置" :canExpan="false">
|
||||||
|
<Row :gutter="24">
|
||||||
|
<Col :span="14">
|
||||||
|
<BasicForm @register="register" />
|
||||||
|
</Col>
|
||||||
|
<Col :span="10">
|
||||||
|
<div class="change-avatar">
|
||||||
|
<div class="mb-2">头像</div>
|
||||||
|
<CropperAvatar
|
||||||
|
:value="avatar"
|
||||||
|
btnText="更换头像"
|
||||||
|
:btnProps="{ preIcon: 'ant-design:cloud-upload-outlined' }"
|
||||||
|
@change="updateAvatar"
|
||||||
|
width="150"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Button type="primary" @click="handleSubmit"> 更新基本信息 </Button>
|
||||||
|
</CollapseContainer>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Button, Row, Col } from 'ant-design-vue'
|
||||||
|
import { computed, onMounted } from 'vue'
|
||||||
|
import { BasicForm, useForm } from '@/components/Form/index'
|
||||||
|
import { CollapseContainer } from '@/components/Container'
|
||||||
|
import { CropperAvatar } from '@/components/Cropper'
|
||||||
|
import { useMessage } from '@/hooks/web/useMessage'
|
||||||
|
import headerImg from '@/assets/images/header.jpg'
|
||||||
|
import { baseSetschemas } from './data'
|
||||||
|
import { useUserStore } from '@/store/modules/user'
|
||||||
|
import { getUserProfileApi, updateUserProfileApi, uploadAvatarApi } from '@/api/base/profile'
|
||||||
|
|
||||||
|
const { createMessage } = useMessage()
|
||||||
|
const userStore = useUserStore()
|
||||||
|
|
||||||
|
const [register, { setFieldsValue, validate }] = useForm({
|
||||||
|
labelWidth: 120,
|
||||||
|
schemas: baseSetschemas,
|
||||||
|
showActionButtonGroup: false
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const data = await getUserProfileApi()
|
||||||
|
setFieldsValue(data)
|
||||||
|
})
|
||||||
|
|
||||||
|
const avatar = computed(() => {
|
||||||
|
const { avatar } = userStore.getUserInfo.user
|
||||||
|
return avatar || headerImg
|
||||||
|
})
|
||||||
|
|
||||||
|
async function updateAvatar({ src, data }) {
|
||||||
|
await uploadAvatarApi({ avatarFile: data })
|
||||||
|
const userinfo = userStore.getUserInfo
|
||||||
|
userinfo.user.avatar = src
|
||||||
|
userStore.setUserInfo(userinfo)
|
||||||
|
console.log('data', data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSubmit() {
|
||||||
|
try {
|
||||||
|
const values = await validate()
|
||||||
|
await updateUserProfileApi(values)
|
||||||
|
} finally {
|
||||||
|
createMessage.success('更新成功!')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.change-avatar {
|
||||||
|
img {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,34 @@
|
||||||
|
<template>
|
||||||
|
<CollapseContainer title="新消息通知" :canExpan="false">
|
||||||
|
<List>
|
||||||
|
<template v-for="item in msgNotifyList" :key="item.key">
|
||||||
|
<ListItem>
|
||||||
|
<ListItemMeta>
|
||||||
|
<template #title>
|
||||||
|
{{ item.title }}
|
||||||
|
<Switch class="extra" checked-children="开" un-checked-children="关" default-checked />
|
||||||
|
</template>
|
||||||
|
<template #description>
|
||||||
|
<div>{{ item.description }}</div>
|
||||||
|
</template>
|
||||||
|
</ListItemMeta>
|
||||||
|
</ListItem>
|
||||||
|
</template>
|
||||||
|
</List>
|
||||||
|
</CollapseContainer>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { List, Switch } from 'ant-design-vue'
|
||||||
|
import { CollapseContainer } from '@/components/Container/index'
|
||||||
|
import { msgNotifyList } from './data'
|
||||||
|
|
||||||
|
const ListItem = List.Item
|
||||||
|
const ListItemMeta = List.Item.Meta
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.extra {
|
||||||
|
float: right;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-right: 30px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,43 @@
|
||||||
|
<template>
|
||||||
|
<BasicModal v-bind="$attrs" @register="registerModal" :title="title" @ok="handleSubmit">
|
||||||
|
<BasicForm @register="registerForm" />
|
||||||
|
</BasicModal>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup name="PasswordModel">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { BasicModal, useModalInner } from '@/components/Modal'
|
||||||
|
import { BasicForm, useForm } from '@/components/Form'
|
||||||
|
import { passwordSchema } from './data'
|
||||||
|
import { updateUserPwdApi } from '@/api/base/profile'
|
||||||
|
|
||||||
|
const emit = defineEmits(['success', 'register'])
|
||||||
|
|
||||||
|
const title = ref('修改密码')
|
||||||
|
|
||||||
|
const [registerForm, { resetFields, validate }] = useForm({
|
||||||
|
labelWidth: 100,
|
||||||
|
baseColProps: { span: 24 },
|
||||||
|
schemas: passwordSchema,
|
||||||
|
showActionButtonGroup: false,
|
||||||
|
actionColOptions: {
|
||||||
|
span: 23
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const [registerModal, { setModalProps, closeModal }] = useModalInner(() => {
|
||||||
|
resetFields()
|
||||||
|
setModalProps({ confirmLoading: false })
|
||||||
|
})
|
||||||
|
|
||||||
|
async function handleSubmit() {
|
||||||
|
try {
|
||||||
|
const values = await validate()
|
||||||
|
await updateUserPwdApi(values.oldPassword, values.newPassword)
|
||||||
|
setModalProps({ confirmLoading: true })
|
||||||
|
closeModal()
|
||||||
|
emit('success')
|
||||||
|
} finally {
|
||||||
|
setModalProps({ confirmLoading: false })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,55 @@
|
||||||
|
<template>
|
||||||
|
<CollapseContainer title="安全设置" :canExpan="false">
|
||||||
|
<List>
|
||||||
|
<template v-for="item in secureSettingList" :key="item.key">
|
||||||
|
<ListItem>
|
||||||
|
<ListItemMeta>
|
||||||
|
<template #title>
|
||||||
|
{{ item.title }}
|
||||||
|
<div class="extra" v-if="item.extra">
|
||||||
|
<a-button type="link" @click="handleEdit(item.title)">{{ item.extra }}</a-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #description>
|
||||||
|
<div>{{ item.description }}</div>
|
||||||
|
</template>
|
||||||
|
</ListItemMeta>
|
||||||
|
</ListItem>
|
||||||
|
</template>
|
||||||
|
</List>
|
||||||
|
</CollapseContainer>
|
||||||
|
<PasswordModel @register="registerModal" @success="handleSuccess" />
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { List } from 'ant-design-vue'
|
||||||
|
import { CollapseContainer } from '@/components/Container/index'
|
||||||
|
import { secureSettingList } from './data'
|
||||||
|
import { useModal } from '@/components/Modal'
|
||||||
|
import { useMessage } from '@/hooks/web/useMessage'
|
||||||
|
import PasswordModel from './PasswordModel.vue'
|
||||||
|
|
||||||
|
const ListItem = List.Item
|
||||||
|
const ListItemMeta = List.Item.Meta
|
||||||
|
|
||||||
|
const { createMessage } = useMessage()
|
||||||
|
const [registerModal, { openModal }] = useModal()
|
||||||
|
|
||||||
|
function handleEdit(title: string) {
|
||||||
|
if (title == '账户密码') {
|
||||||
|
openModal(true, {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function handleSuccess() {
|
||||||
|
createMessage.success('更新成功!')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.extra {
|
||||||
|
float: right;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-right: 30px;
|
||||||
|
font-weight: normal;
|
||||||
|
color: #1890ff;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,189 @@
|
||||||
|
import { FormSchema } from '@/components/Form/index'
|
||||||
|
import { useI18n } from '@/hooks/web/useI18n'
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
export interface ListItem {
|
||||||
|
key: string
|
||||||
|
title: string
|
||||||
|
description: string
|
||||||
|
extra?: string
|
||||||
|
avatar?: string
|
||||||
|
color?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// tab的list
|
||||||
|
export const settingList = [
|
||||||
|
{
|
||||||
|
key: '1',
|
||||||
|
name: '基本设置',
|
||||||
|
component: 'BaseSetting'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '2',
|
||||||
|
name: '安全设置',
|
||||||
|
component: 'SecureSetting'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '3',
|
||||||
|
name: '账号绑定',
|
||||||
|
component: 'AccountBind'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '4',
|
||||||
|
name: '新消息通知',
|
||||||
|
component: 'MsgNotify'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
// 基础设置 form
|
||||||
|
export const baseSetschemas: FormSchema[] = [
|
||||||
|
{
|
||||||
|
field: 'nickname',
|
||||||
|
component: 'Input',
|
||||||
|
label: t('profile.user.nickname'),
|
||||||
|
colProps: { span: 18 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'mobile',
|
||||||
|
component: 'Input',
|
||||||
|
label: t('profile.user.mobile'),
|
||||||
|
colProps: { span: 18 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'email',
|
||||||
|
component: 'Input',
|
||||||
|
label: t('profile.user.email'),
|
||||||
|
colProps: { span: 18 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'sex',
|
||||||
|
component: 'RadioGroup',
|
||||||
|
componentProps: {
|
||||||
|
options: [
|
||||||
|
{ label: '男', value: 1 },
|
||||||
|
{ label: '女', value: 2 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
label: t('profile.user.sex'),
|
||||||
|
colProps: { span: 18 }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
// 安全设置 list
|
||||||
|
export const secureSettingList: ListItem[] = [
|
||||||
|
{
|
||||||
|
key: '1',
|
||||||
|
title: '账户密码',
|
||||||
|
description: '当前密码强度::强',
|
||||||
|
extra: '修改'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '2',
|
||||||
|
title: '密保手机',
|
||||||
|
description: '已绑定手机::138****8293',
|
||||||
|
extra: '修改'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '3',
|
||||||
|
title: '密保问题',
|
||||||
|
description: '未设置密保问题,密保问题可有效保护账户安全',
|
||||||
|
extra: '修改'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '4',
|
||||||
|
title: '备用邮箱',
|
||||||
|
description: '已绑定邮箱::ant***sign.com',
|
||||||
|
extra: '修改'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '5',
|
||||||
|
title: 'MFA 设备',
|
||||||
|
description: '未绑定 MFA 设备,绑定后,可以进行二次确认',
|
||||||
|
extra: '修改'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
// 账号绑定 list
|
||||||
|
export const accountBindList: ListItem[] = [
|
||||||
|
{
|
||||||
|
key: '20',
|
||||||
|
title: '钉钉',
|
||||||
|
description: '当前未绑定钉钉账号',
|
||||||
|
extra: '绑定',
|
||||||
|
avatar: 'ri:dingding-fill',
|
||||||
|
color: '#2eabff'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '30',
|
||||||
|
title: '企业微信',
|
||||||
|
description: '当前未绑定企业微信',
|
||||||
|
extra: '绑定',
|
||||||
|
avatar: 'ri:wechat-line',
|
||||||
|
color: '#2eabff'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
// 新消息通知 list
|
||||||
|
export const msgNotifyList: ListItem[] = [
|
||||||
|
{
|
||||||
|
key: '1',
|
||||||
|
title: '账户密码',
|
||||||
|
description: '其他用户的消息将以站内信的形式通知'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '2',
|
||||||
|
title: '系统消息',
|
||||||
|
description: '系统消息将以站内信的形式通知'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '3',
|
||||||
|
title: '待办任务',
|
||||||
|
description: '待办任务将以站内信的形式通知'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
export const passwordSchema: FormSchema[] = [
|
||||||
|
{
|
||||||
|
field: 'oldPassword',
|
||||||
|
label: '当前密码',
|
||||||
|
component: 'InputPassword',
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'newPassword',
|
||||||
|
label: '新密码',
|
||||||
|
component: 'StrengthMeter',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '新密码'
|
||||||
|
},
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入新密码'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'confirmPassword',
|
||||||
|
label: '确认密码',
|
||||||
|
component: 'InputPassword',
|
||||||
|
|
||||||
|
dynamicRules: ({ values }) => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
validator: (_, value) => {
|
||||||
|
if (!value) {
|
||||||
|
return Promise.reject('密码不能为空')
|
||||||
|
}
|
||||||
|
if (value !== values.newPassword) {
|
||||||
|
return Promise.reject('两次输入的密码不一致!')
|
||||||
|
}
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,40 @@
|
||||||
|
<template>
|
||||||
|
<ScrollContainer>
|
||||||
|
<div ref="wrapperRef" class="account-setting">
|
||||||
|
<Tabs tab-position="left" :tabBarStyle="tabBarStyle">
|
||||||
|
<template v-for="item in settingList" :key="item.key">
|
||||||
|
<TabPane :tab="item.name">
|
||||||
|
<BaseSetting v-if="item.component == 'BaseSetting'" />
|
||||||
|
<SecureSetting v-if="item.component == 'SecureSetting'" />
|
||||||
|
<AccountBind v-if="item.component == 'AccountBind'" />
|
||||||
|
<MsgNotify v-if="item.component == 'MsgNotify'" />
|
||||||
|
</TabPane>
|
||||||
|
</template>
|
||||||
|
</Tabs>
|
||||||
|
</div>
|
||||||
|
</ScrollContainer>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Tabs, TabPane } from 'ant-design-vue'
|
||||||
|
import { ScrollContainer } from '@/components/Container/index'
|
||||||
|
import { settingList } from './data'
|
||||||
|
import BaseSetting from './BaseSetting.vue'
|
||||||
|
import SecureSetting from './SecureSetting.vue'
|
||||||
|
import AccountBind from './AccountBind.vue'
|
||||||
|
import MsgNotify from './MsgNotify.vue'
|
||||||
|
const tabBarStyle = { width: '220px' }
|
||||||
|
</script>
|
||||||
|
<style lang="less">
|
||||||
|
.account-setting {
|
||||||
|
margin: 12px;
|
||||||
|
background-color: @component-background;
|
||||||
|
|
||||||
|
.base-title {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-tabs-tab-active {
|
||||||
|
background-color: @item-active-bg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,8 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="lg:flex">
|
<div class="lg:flex">
|
||||||
<Avatar :src="userinfo.avatar || headerImg" :size="72" class="!mx-auto !block" />
|
<Avatar :src="userinfo.user.avatar || headerImg" :size="72" class="!mx-auto !block" />
|
||||||
<div class="md:ml-6 flex flex-col justify-center md:mt-0 mt-2">
|
<div class="md:ml-6 flex flex-col justify-center md:mt-0 mt-2">
|
||||||
<h1 class="md:text-lg text-md">早安, {{ userinfo.realName }}, 开始您一天的工作吧!</h1>
|
<h1 class="md:text-lg text-md">早安, {{ userinfo.user.nickname }}, 开始您一天的工作吧!</h1>
|
||||||
<span class="text-secondary"> 今日晴,20℃ - 32℃! </span>
|
<span class="text-secondary"> 今日晴,20℃ - 32℃! </span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-1 justify-end md:mt-0 mt-4">
|
<div class="flex flex-1 justify-end md:mt-0 mt-4">
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
<template>
|
|
||||||
<BasicModal :width="800" :title="t('sys.errorLog.tableActionDesc')" v-bind="$attrs">
|
|
||||||
<Description :data="info" @register="register" />
|
|
||||||
</BasicModal>
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import type { ErrorLogInfo } from '@/types/store'
|
|
||||||
import { BasicModal } from '@/components/Modal'
|
|
||||||
import { Description, useDescription } from '@/components/Description'
|
|
||||||
import { useI18n } from '@/hooks/web/useI18n'
|
|
||||||
import { getDescSchema } from './data'
|
|
||||||
|
|
||||||
defineProps({
|
|
||||||
info: {
|
|
||||||
type: Object as PropType<ErrorLogInfo>,
|
|
||||||
default: null
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
|
||||||
|
|
||||||
const [register] = useDescription({
|
|
||||||
column: 2,
|
|
||||||
schema: getDescSchema()!
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,67 +0,0 @@
|
||||||
import { Tag } from 'ant-design-vue'
|
|
||||||
import { BasicColumn } from '@/components/Table'
|
|
||||||
import { ErrorTypeEnum } from '@/enums/exceptionEnum'
|
|
||||||
import { useI18n } from '@/hooks/web/useI18n'
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
|
||||||
|
|
||||||
export function getColumns(): BasicColumn[] {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
dataIndex: 'type',
|
|
||||||
title: t('sys.errorLog.tableColumnType'),
|
|
||||||
width: 80,
|
|
||||||
customRender: ({ text }) => {
|
|
||||||
const color =
|
|
||||||
text === ErrorTypeEnum.VUE
|
|
||||||
? 'green'
|
|
||||||
: text === ErrorTypeEnum.RESOURCE
|
|
||||||
? 'cyan'
|
|
||||||
: text === ErrorTypeEnum.PROMISE
|
|
||||||
? 'blue'
|
|
||||||
: ErrorTypeEnum.AJAX
|
|
||||||
? 'red'
|
|
||||||
: 'purple'
|
|
||||||
return <Tag color={color}>{() => text}</Tag>
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
dataIndex: 'url',
|
|
||||||
title: 'URL',
|
|
||||||
width: 200
|
|
||||||
},
|
|
||||||
{
|
|
||||||
dataIndex: 'time',
|
|
||||||
title: t('sys.errorLog.tableColumnDate'),
|
|
||||||
width: 160
|
|
||||||
},
|
|
||||||
{
|
|
||||||
dataIndex: 'file',
|
|
||||||
title: t('sys.errorLog.tableColumnFile'),
|
|
||||||
width: 200
|
|
||||||
},
|
|
||||||
{
|
|
||||||
dataIndex: 'name',
|
|
||||||
title: 'Name',
|
|
||||||
width: 200
|
|
||||||
},
|
|
||||||
{
|
|
||||||
dataIndex: 'message',
|
|
||||||
title: t('sys.errorLog.tableColumnMsg'),
|
|
||||||
width: 300
|
|
||||||
},
|
|
||||||
{
|
|
||||||
dataIndex: 'stack',
|
|
||||||
title: t('sys.errorLog.tableColumnStackMsg')
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getDescSchema(): any {
|
|
||||||
return getColumns().map((column) => {
|
|
||||||
return {
|
|
||||||
field: column.dataIndex!,
|
|
||||||
label: column.title
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,97 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="p-4">
|
|
||||||
<template v-for="src in imgList" :key="src">
|
|
||||||
<img :src="src" v-show="false" alt="" />
|
|
||||||
</template>
|
|
||||||
<DetailModal :info="rowInfo" @register="registerModal" />
|
|
||||||
<BasicTable @register="register" class="error-handle-table">
|
|
||||||
<template #toolbar>
|
|
||||||
<a-button @click="fireVueError" type="primary">
|
|
||||||
{{ t('sys.errorLog.fireVueError') }}
|
|
||||||
</a-button>
|
|
||||||
<a-button @click="fireResourceError" type="primary">
|
|
||||||
{{ t('sys.errorLog.fireResourceError') }}
|
|
||||||
</a-button>
|
|
||||||
<a-button @click="fireAjaxError" type="primary">
|
|
||||||
{{ t('sys.errorLog.fireAjaxError') }}
|
|
||||||
</a-button>
|
|
||||||
</template>
|
|
||||||
<template #bodyCell="{ column, record }">
|
|
||||||
<template v-if="column.key === 'action'">
|
|
||||||
<TableAction
|
|
||||||
:actions="[
|
|
||||||
{
|
|
||||||
label: t('sys.errorLog.tableActionDesc'),
|
|
||||||
onClick: handleDetail.bind(null, record)
|
|
||||||
}
|
|
||||||
]"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
</BasicTable>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import type { ErrorLogInfo } from '@/types/store'
|
|
||||||
import { watch, ref, nextTick } from 'vue'
|
|
||||||
import DetailModal from './DetailModal.vue'
|
|
||||||
import { BasicTable, useTable, TableAction } from '@/components/Table'
|
|
||||||
import { useModal } from '@/components/Modal'
|
|
||||||
import { useMessage } from '@/hooks/web/useMessage'
|
|
||||||
import { useI18n } from '@/hooks/web/useI18n'
|
|
||||||
import { useErrorLogStore } from '@/store/modules/errorLog'
|
|
||||||
import { fireErrorApi } from '@/api/demo/error'
|
|
||||||
import { getColumns } from './data'
|
|
||||||
import { cloneDeep } from 'lodash-es'
|
|
||||||
|
|
||||||
const rowInfo = ref<ErrorLogInfo>()
|
|
||||||
const imgList = ref<string[]>([])
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
|
||||||
const errorLogStore = useErrorLogStore()
|
|
||||||
const [register, { setTableData }] = useTable({
|
|
||||||
title: t('sys.errorLog.tableTitle'),
|
|
||||||
columns: getColumns(),
|
|
||||||
actionColumn: {
|
|
||||||
width: 80,
|
|
||||||
title: 'Action',
|
|
||||||
dataIndex: 'action'
|
|
||||||
// slots: { customRender: 'action' },
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const [registerModal, { openModal }] = useModal()
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => errorLogStore.getErrorLogInfoList,
|
|
||||||
(list) => {
|
|
||||||
nextTick(() => {
|
|
||||||
setTableData(cloneDeep(list))
|
|
||||||
})
|
|
||||||
},
|
|
||||||
{
|
|
||||||
immediate: true
|
|
||||||
}
|
|
||||||
)
|
|
||||||
const { createMessage } = useMessage()
|
|
||||||
if (import.meta.env.DEV) {
|
|
||||||
createMessage.info(t('sys.errorLog.enableMessage'))
|
|
||||||
}
|
|
||||||
// 查看详情
|
|
||||||
function handleDetail(row: ErrorLogInfo) {
|
|
||||||
rowInfo.value = row
|
|
||||||
openModal(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
function fireVueError() {
|
|
||||||
throw new Error('fire vue error!')
|
|
||||||
}
|
|
||||||
|
|
||||||
function fireResourceError() {
|
|
||||||
imgList.value.push(`${new Date().getTime()}.png`)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fireAjaxError() {
|
|
||||||
await fireErrorApi()
|
|
||||||
}
|
|
||||||
</script>
|
|
Loading…
Reference in New Issue