29 changed files with 507 additions and 192 deletions
@ -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,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> |
|
Reference in new issue