删除
This commit is contained in:
2
package-lock.json
generated
2
package-lock.json
generated
@@ -765,7 +765,7 @@
|
|||||||
},
|
},
|
||||||
"node_modules/@clack/prompts/node_modules/is-unicode-supported": {
|
"node_modules/@clack/prompts/node_modules/is-unicode-supported": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"dev": true,
|
"extraneous": true,
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
@@ -2,10 +2,8 @@ export * from './user'
|
|||||||
export * from './role'
|
export * from './role'
|
||||||
export * from './menu'
|
export * from './menu'
|
||||||
export * from './dept'
|
export * from './dept'
|
||||||
export * from './notice'
|
|
||||||
export * from './dict'
|
export * from './dict'
|
||||||
export * from './file'
|
export * from './file'
|
||||||
export * from './storage'
|
export * from './storage'
|
||||||
export * from './option'
|
export * from './option'
|
||||||
export * from './user-center'
|
export * from './user-center'
|
||||||
export * from './message'
|
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
import type * as T from './type'
|
|
||||||
import http from '@/utils/http'
|
|
||||||
|
|
||||||
export type * from './type'
|
|
||||||
|
|
||||||
const BASE_URL = '/system/message'
|
|
||||||
|
|
||||||
/** @desc 查询消息列表 */
|
|
||||||
export function listMessage(query: T.MessagePageQuery) {
|
|
||||||
return http.get<PageRes<T.MessageResp[]>>(`${BASE_URL}`, query)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @desc 删除消息 */
|
|
||||||
export function deleteMessage(ids: string | Array<string>) {
|
|
||||||
return http.del(`${BASE_URL}/${ids}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @desc 标记已读 */
|
|
||||||
export function readMessage(ids?: string | Array<string>) {
|
|
||||||
return http.patch(`${BASE_URL}/read`, ids)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @desc 查询未读消息数量 */
|
|
||||||
export function getUnreadMessageCount() {
|
|
||||||
return http.get(`${BASE_URL}/unread`)
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
import type * as T from './type'
|
|
||||||
import http from '@/utils/http'
|
|
||||||
|
|
||||||
export type * from './type'
|
|
||||||
|
|
||||||
const BASE_URL = '/system/notice'
|
|
||||||
|
|
||||||
/** @desc 查询公告列表 */
|
|
||||||
export function listNotice(query: T.NoticePageQuery) {
|
|
||||||
return http.get<PageRes<T.NoticeResp[]>>(`${BASE_URL}`, query)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @desc 查询公告详情 */
|
|
||||||
export function getNotice(id: string) {
|
|
||||||
return http.get<T.NoticeResp>(`${BASE_URL}/${id}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @desc 新增公告 */
|
|
||||||
export function addNotice(data: any) {
|
|
||||||
return http.post(BASE_URL, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @desc 修改公告 */
|
|
||||||
export function updateNotice(data: any, id: string) {
|
|
||||||
return http.put(`${BASE_URL}/${id}`, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @desc 删除公告 */
|
|
||||||
export function deleteNotice(ids: string | Array<number>) {
|
|
||||||
return http.del(`${BASE_URL}/${ids}`)
|
|
||||||
}
|
|
||||||
@@ -1,124 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="message">
|
|
||||||
<a-list :loading="loading">
|
|
||||||
<template #header>通知</template>
|
|
||||||
<a-list-item v-for="item in messageList" :key="item.id">
|
|
||||||
<div class="content-wrapper" @click="open">
|
|
||||||
<div class="content">{{ item.title }}</div>
|
|
||||||
<div class="date">{{ item.createTime }}</div>
|
|
||||||
</div>
|
|
||||||
</a-list-item>
|
|
||||||
<template #footer>
|
|
||||||
<a class="more-btn" @click="open">查看更多
|
|
||||||
<icon-right />
|
|
||||||
</a>
|
|
||||||
<a class="read-all-btn" @click="readAll">全部已读</a>
|
|
||||||
</template>
|
|
||||||
</a-list>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { onMounted } from 'vue'
|
|
||||||
import { type MessageResp, listMessage, readMessage } from '@/apis'
|
|
||||||
|
|
||||||
const emit = defineEmits<{
|
|
||||||
(e: 'readall-success'): void
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const queryParam = reactive({
|
|
||||||
isRead: false,
|
|
||||||
sort: ['createTime,desc'],
|
|
||||||
page: 1,
|
|
||||||
size: 5,
|
|
||||||
})
|
|
||||||
|
|
||||||
const messageList = ref<MessageResp[]>()
|
|
||||||
const loading = ref(false)
|
|
||||||
// 查询消息数据
|
|
||||||
const getMessageData = async () => {
|
|
||||||
try {
|
|
||||||
loading.value = true
|
|
||||||
const { data } = await listMessage(queryParam)
|
|
||||||
messageList.value = data.list
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 打开消息中心
|
|
||||||
const open = () => {
|
|
||||||
window.open('/setting/message')
|
|
||||||
}
|
|
||||||
|
|
||||||
// 全部已读
|
|
||||||
const readAll = async () => {
|
|
||||||
await readMessage()
|
|
||||||
await getMessageData()
|
|
||||||
emit('readall-success')
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
getMessageData()
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.message {
|
|
||||||
height: auto;
|
|
||||||
max-height: calc(100% - 51px);
|
|
||||||
width: 300px;
|
|
||||||
|
|
||||||
.content-wrapper {
|
|
||||||
padding: 10px;
|
|
||||||
border-radius: var(--border-radius-medium);
|
|
||||||
cursor: pointer;
|
|
||||||
.content {
|
|
||||||
font-size: 12px;
|
|
||||||
height: 20px;
|
|
||||||
max-width: 265px;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
width: 265px;
|
|
||||||
}
|
|
||||||
.date {
|
|
||||||
color: var(--color-text-4);
|
|
||||||
font-size: 12px;
|
|
||||||
margin-top: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: var(--color-bg-4);
|
|
||||||
color: rgb(var(--arcoblue-6));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.arco-list) {
|
|
||||||
border-radius: var(--border-radius-medium);
|
|
||||||
.arco-list-header {
|
|
||||||
font-size: 13px;
|
|
||||||
padding: 9px 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.arco-list-content {
|
|
||||||
max-height: 184px;
|
|
||||||
|
|
||||||
.arco-list-item {
|
|
||||||
padding: 6px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.arco-list-footer {
|
|
||||||
font-size: 12px;
|
|
||||||
color: rgb(var(--arcoblue-6));
|
|
||||||
padding: 9px 12px;
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
.more-btn {
|
|
||||||
margin-right: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -26,9 +26,6 @@
|
|||||||
</template>
|
</template>
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-badge>
|
</a-badge>
|
||||||
<template #content>
|
|
||||||
<Message @readall-success="getMessageCount" />
|
|
||||||
</template>
|
|
||||||
</a-popover>
|
</a-popover>
|
||||||
|
|
||||||
<!-- 全屏切换组件 -->
|
<!-- 全屏切换组件 -->
|
||||||
@@ -74,12 +71,9 @@
|
|||||||
import { Modal } from '@arco-design/web-vue'
|
import { Modal } from '@arco-design/web-vue'
|
||||||
import { useFullscreen } from '@vueuse/core'
|
import { useFullscreen } from '@vueuse/core'
|
||||||
import { onMounted, ref } from 'vue'
|
import { onMounted, ref } from 'vue'
|
||||||
import Message from './Message.vue'
|
|
||||||
import SettingDrawer from './SettingDrawer.vue'
|
import SettingDrawer from './SettingDrawer.vue'
|
||||||
import Search from './Search.vue'
|
import Search from './Search.vue'
|
||||||
import { getUnreadMessageCount } from '@/apis'
|
|
||||||
import { useUserStore } from '@/stores'
|
import { useUserStore } from '@/stores'
|
||||||
import { getToken } from '@/utils/auth'
|
|
||||||
import { useBreakpoint } from '@/hooks'
|
import { useBreakpoint } from '@/hooks'
|
||||||
|
|
||||||
defineOptions({ name: 'HeaderRight' })
|
defineOptions({ name: 'HeaderRight' })
|
||||||
@@ -113,15 +107,6 @@ const initWebSocket = (token: string) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询未读消息数量
|
|
||||||
const getMessageCount = async () => {
|
|
||||||
const { data } = await getUnreadMessageCount()
|
|
||||||
unreadMessageCount.value = data.total
|
|
||||||
const token = getToken()
|
|
||||||
if (token) {
|
|
||||||
initWebSocket(token)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const { isFullscreen, toggle } = useFullscreen()
|
const { isFullscreen, toggle } = useFullscreen()
|
||||||
|
|
||||||
@@ -148,9 +133,6 @@ const logout = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
getMessageCount()
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -1,146 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="gi_table_page">
|
|
||||||
<GiTable
|
|
||||||
row-key="id"
|
|
||||||
title="消息中心"
|
|
||||||
:data="dataList"
|
|
||||||
:columns="columns"
|
|
||||||
:loading="loading"
|
|
||||||
:scroll="{ x: '100%', y: '100%', minWidth: 1000 }"
|
|
||||||
:pagination="pagination"
|
|
||||||
:disabled-tools="['size', 'setting']"
|
|
||||||
:disabled-column-keys="['name']"
|
|
||||||
:row-selection="{ type: 'checkbox', showCheckedAll: true }"
|
|
||||||
:selected-keys="selectedKeys"
|
|
||||||
@select-all="selectAll"
|
|
||||||
@select="select"
|
|
||||||
@refresh="search"
|
|
||||||
>
|
|
||||||
<template #toolbar-left>
|
|
||||||
<a-input v-model="queryForm.title" placeholder="请输入标题" allow-clear @change="search">
|
|
||||||
<template #prefix><icon-search /></template>
|
|
||||||
</a-input>
|
|
||||||
<a-select
|
|
||||||
v-model="queryForm.isRead"
|
|
||||||
placeholder="请选择状态"
|
|
||||||
allow-clear
|
|
||||||
style="width: 150px"
|
|
||||||
@change="search"
|
|
||||||
>
|
|
||||||
<a-option :value="false">未读</a-option>
|
|
||||||
<a-option :value="true">已读</a-option>
|
|
||||||
</a-select>
|
|
||||||
<a-button @click="reset">
|
|
||||||
<template #icon><icon-refresh /></template>
|
|
||||||
<template #default>重置</template>
|
|
||||||
</a-button>
|
|
||||||
</template>
|
|
||||||
<template #toolbar-right>
|
|
||||||
<a-button type="primary" status="danger" :disabled="!selectedKeys.length" :title="!selectedKeys.length ? '请选择' : ''" @click="onDelete">
|
|
||||||
<template #icon><icon-delete /></template>
|
|
||||||
<template #default>删除</template>
|
|
||||||
</a-button>
|
|
||||||
<a-button type="primary" :disabled="!selectedKeys.length" :title="!selectedKeys.length ? '请选择' : ''" @click="onRead">
|
|
||||||
<template #default>标记为已读</template>
|
|
||||||
</a-button>
|
|
||||||
<a-button type="primary" :disabled="selectedKeys.length" :title="!selectedKeys.length ? '请选择' : ''" @click="onReadAll">
|
|
||||||
<template #default>全部已读</template>
|
|
||||||
</a-button>
|
|
||||||
</template>
|
|
||||||
<template #title="{ record }">
|
|
||||||
<a-tooltip :content="record.content"><span>{{ record.title }}</span></a-tooltip>
|
|
||||||
</template>
|
|
||||||
<template #isRead="{ record }">
|
|
||||||
<a-tag :color="record.isRead ? '' : 'arcoblue'">
|
|
||||||
{{ record.isRead ? '已读' : '未读' }}
|
|
||||||
</a-tag>
|
|
||||||
</template>
|
|
||||||
<template #type="{ record }">
|
|
||||||
<GiCellTag :value="record.type" :dict="message_type" />
|
|
||||||
</template>
|
|
||||||
</GiTable>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { Message, Modal } from '@arco-design/web-vue'
|
|
||||||
import { type MessageQuery, deleteMessage, listMessage, readMessage } from '@/apis'
|
|
||||||
import type { TableInstanceColumns } from '@/components/GiTable/type'
|
|
||||||
import { useTable } from '@/hooks'
|
|
||||||
import { useDict } from '@/hooks/app'
|
|
||||||
|
|
||||||
defineOptions({ name: 'SystemMessage' })
|
|
||||||
|
|
||||||
const { message_type } = useDict('message_type')
|
|
||||||
|
|
||||||
const queryForm = reactive<MessageQuery>({
|
|
||||||
sort: ['createTime,desc'],
|
|
||||||
})
|
|
||||||
|
|
||||||
const {
|
|
||||||
tableData: dataList,
|
|
||||||
loading,
|
|
||||||
pagination,
|
|
||||||
selectedKeys,
|
|
||||||
select,
|
|
||||||
selectAll,
|
|
||||||
search,
|
|
||||||
handleDelete,
|
|
||||||
} = useTable((page) => listMessage({ ...queryForm, ...page }), { immediate: true })
|
|
||||||
|
|
||||||
const columns: TableInstanceColumns[] = [
|
|
||||||
{
|
|
||||||
title: '序号',
|
|
||||||
width: 66,
|
|
||||||
align: 'center',
|
|
||||||
render: ({ rowIndex }) => h('span', {}, rowIndex + 1 + (pagination.current - 1) * pagination.pageSize),
|
|
||||||
},
|
|
||||||
{ title: '标题', dataIndex: 'title', slotName: 'title', minWidth: 100, ellipsis: true, tooltip: true },
|
|
||||||
{ title: '状态', dataIndex: 'isRead', slotName: 'isRead', align: 'center' },
|
|
||||||
{ title: '时间', dataIndex: 'createTime', width: 180 },
|
|
||||||
{ title: '类型', dataIndex: 'type', slotName: 'type', width: 180, ellipsis: true, tooltip: true },
|
|
||||||
]
|
|
||||||
|
|
||||||
// 重置
|
|
||||||
const reset = () => {
|
|
||||||
queryForm.title = undefined
|
|
||||||
queryForm.type = undefined
|
|
||||||
queryForm.isRead = undefined
|
|
||||||
search()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除
|
|
||||||
const onDelete = () => {
|
|
||||||
if (!selectedKeys.value.length) {
|
|
||||||
return Message.warning('请选择数据')
|
|
||||||
}
|
|
||||||
return handleDelete(() => deleteMessage(selectedKeys.value), { showModal: false })
|
|
||||||
}
|
|
||||||
|
|
||||||
// 标记为已读
|
|
||||||
const onRead = async () => {
|
|
||||||
if (!selectedKeys.value.length) {
|
|
||||||
return Message.warning('请选择数据')
|
|
||||||
}
|
|
||||||
await readMessage(selectedKeys.value)
|
|
||||||
Message.success('操作成功')
|
|
||||||
search()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 全部已读
|
|
||||||
const onReadAll = async () => {
|
|
||||||
Modal.warning({
|
|
||||||
title: '全部已读',
|
|
||||||
content: '确定要标记全部消息为已读吗?',
|
|
||||||
hideCancel: false,
|
|
||||||
maskClosable: false,
|
|
||||||
onOk: async () => {
|
|
||||||
await readMessage([])
|
|
||||||
Message.success('操作成功')
|
|
||||||
search()
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
<template>
|
|
||||||
<a-modal v-model:visible="visible" :width="width >= 600 ? 'auto' : '100%'" :footer="false" draggable @close="reset">
|
|
||||||
<a-typography :style="{ marginTop: '-40px', textAlign: 'center' }">
|
|
||||||
<a-typography-title>
|
|
||||||
{{ dataDetail?.title }}
|
|
||||||
</a-typography-title>
|
|
||||||
<a-typography-paragraph>
|
|
||||||
<div class="meta-data">
|
|
||||||
<a-space>
|
|
||||||
<span>
|
|
||||||
<icon-user class="icon" />
|
|
||||||
<span class="label">发布人:</span>
|
|
||||||
<span>{{ dataDetail?.createUserString }}</span>
|
|
||||||
</span>
|
|
||||||
<a-divider direction="vertical" />
|
|
||||||
<span>
|
|
||||||
<icon-history class="icon" />
|
|
||||||
<span class="label">发布时间:</span>
|
|
||||||
<span>{{ dataDetail?.effectiveTime ? dataDetail?.effectiveTime : dataDetail?.createTime }}</span>
|
|
||||||
</span>
|
|
||||||
</a-space>
|
|
||||||
</div>
|
|
||||||
</a-typography-paragraph>
|
|
||||||
</a-typography>
|
|
||||||
<a-divider />
|
|
||||||
<AiEditor :model-value="dataDetail?.content" />
|
|
||||||
<a-divider />
|
|
||||||
<div v-if="dataDetail?.updateTime" class="update-time-row">
|
|
||||||
<span>
|
|
||||||
<icon-schedule class="icon" />
|
|
||||||
<span>最后更新于:</span>
|
|
||||||
<span>{{ dataDetail?.updateTime }}</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</a-modal>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { useWindowSize } from '@vueuse/core'
|
|
||||||
import AiEditor from './detail/components/index.vue'
|
|
||||||
import { type NoticeResp, getNotice } from '@/apis/system'
|
|
||||||
|
|
||||||
const { width } = useWindowSize()
|
|
||||||
const dataDetail = ref<NoticeResp>({
|
|
||||||
content: '',
|
|
||||||
})
|
|
||||||
const visible = ref(false)
|
|
||||||
// 详情
|
|
||||||
const onDetail = async (id: string) => {
|
|
||||||
const { data } = await getNotice(id)
|
|
||||||
dataDetail.value = data
|
|
||||||
visible.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重置
|
|
||||||
const reset = () => {
|
|
||||||
dataDetail.value = {
|
|
||||||
content: '',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
defineExpose({ onDetail })
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.arco-link {
|
|
||||||
color: rgb(var(--gray-8));
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
margin-right: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.update-time-row {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,209 +0,0 @@
|
|||||||
<!-- 未完善 -->
|
|
||||||
<template>
|
|
||||||
<div ref="divRef" class="container">
|
|
||||||
<div class="aie-container">
|
|
||||||
<div class="aie-header-panel">
|
|
||||||
<div class="aie-container-header"></div>
|
|
||||||
</div>
|
|
||||||
<div class="aie-main">
|
|
||||||
<div class="aie-directory-content">
|
|
||||||
<div class="aie-directory">
|
|
||||||
<h5>目录</h5>
|
|
||||||
<div id="outline">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="aie-container-panel">
|
|
||||||
<div class="aie-container-main"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="aie-container-footer"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { AiEditor, type AiEditorOptions } from 'aieditor'
|
|
||||||
import 'aieditor/dist/style.css'
|
|
||||||
import { useAppStore } from '@/stores'
|
|
||||||
|
|
||||||
defineOptions({ name: 'AiEditor' })
|
|
||||||
const props = defineProps<{
|
|
||||||
modelValue: string
|
|
||||||
options?: AiEditorOptions
|
|
||||||
}>()
|
|
||||||
const emit = defineEmits<(e: 'update:modelValue', value: string) => void>()
|
|
||||||
const appStore = useAppStore()
|
|
||||||
const divRef = ref<any>()
|
|
||||||
const aieditor = ref<AiEditor | null>(null)
|
|
||||||
const updateOutLine = (editor: AiEditor) => {
|
|
||||||
const outlineContainer = document.querySelector('#outline')
|
|
||||||
while (outlineContainer?.firstChild) {
|
|
||||||
outlineContainer.removeChild(outlineContainer.firstChild)
|
|
||||||
}
|
|
||||||
const outlines = editor.getOutline()
|
|
||||||
for (const outline of outlines) {
|
|
||||||
const child = document.createElement('div')
|
|
||||||
child.classList.add(`aie-title${outline.level}`)
|
|
||||||
child.style.marginLeft = `${14 * (outline.level - 1)}px`
|
|
||||||
child.style.padding = `4px 0`
|
|
||||||
child.innerHTML = `<a href="#${outline.id}">${outline.text}</a>`
|
|
||||||
child.addEventListener('click', (e) => {
|
|
||||||
e.preventDefault()
|
|
||||||
const el = editor.innerEditor.view.dom.querySelector(`#${outline.id}`) as HTMLElement
|
|
||||||
el.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' })
|
|
||||||
setTimeout(() => {
|
|
||||||
editor.focusPos(outline.pos + outline.size - 1)
|
|
||||||
}, 1000)
|
|
||||||
})
|
|
||||||
outlineContainer?.appendChild(child)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const editorConfig = reactive<AiEditorOptions>({
|
|
||||||
element: '',
|
|
||||||
theme: appStore.theme,
|
|
||||||
placeholder: '请输入内容',
|
|
||||||
content: '',
|
|
||||||
draggable: false,
|
|
||||||
onChange: (editor: AiEditor) => {
|
|
||||||
emit('update:modelValue', editor.getHtml())
|
|
||||||
updateOutLine(editor)
|
|
||||||
},
|
|
||||||
onCreated: (editor: AiEditor) => {
|
|
||||||
updateOutLine(editor)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
watch(() => props.modelValue, (value) => {
|
|
||||||
if (value !== aieditor.value?.getHtml()) {
|
|
||||||
aieditor.value?.destroy()
|
|
||||||
editorConfig.content = value
|
|
||||||
aieditor.value = new AiEditor(editorConfig)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const init = () => {
|
|
||||||
editorConfig.element = divRef.value
|
|
||||||
aieditor.value = new AiEditor(editorConfig)
|
|
||||||
}
|
|
||||||
// 挂载阶段
|
|
||||||
onMounted(() => {
|
|
||||||
init()
|
|
||||||
})
|
|
||||||
// 销毁阶段
|
|
||||||
onUnmounted(() => {
|
|
||||||
aieditor.value?.destroy()
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.container {
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aie-header-panel {
|
|
||||||
position: sticky;
|
|
||||||
// top: 51px;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aie-header-panel aie-header>div {
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 10px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aie-container {
|
|
||||||
border: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aie-container-panel {
|
|
||||||
width: calc(100% - 2rem - 2px);
|
|
||||||
max-width: 826.77px;
|
|
||||||
margin: 0rem auto;
|
|
||||||
border: 1px solid var(--color-border-1);
|
|
||||||
background-color: var() rgba($color: var(--color-bg-1), $alpha: 1.0);
|
|
||||||
height: 100%;
|
|
||||||
padding: 1rem;
|
|
||||||
z-index: 99;
|
|
||||||
overflow: auto;
|
|
||||||
box-sizing: border-box;
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aie-main {
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
flex: 1;
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 1rem 0px;
|
|
||||||
background-color: var(--color-bg-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.aie-directory {
|
|
||||||
position: absolute;
|
|
||||||
top: 30px;
|
|
||||||
left: 10px;
|
|
||||||
width: 260px;
|
|
||||||
z-index: 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.aie-directory h5 {
|
|
||||||
// color: #000000c4;
|
|
||||||
font-size: 16px;
|
|
||||||
text-indent: 4px;
|
|
||||||
line-height: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aie-directory a {
|
|
||||||
height: 30px;
|
|
||||||
font-size: 14px;
|
|
||||||
// color: #000000a3;
|
|
||||||
text-indent: 4px;
|
|
||||||
line-height: 30px;
|
|
||||||
text-decoration: none;
|
|
||||||
width: 100%;
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
-o-text-overflow: ellipsis;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aie-directory a:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
// background-color: #334d660f;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aie-title1 {
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
#outline {
|
|
||||||
text-indent: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aie-directory-content {
|
|
||||||
position: sticky;
|
|
||||||
top: 0px
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 1280px) {
|
|
||||||
.aie-directory {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 1400px) {
|
|
||||||
.aie-directory {
|
|
||||||
width: 200px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,212 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div ref="containerRef" class="detail">
|
|
||||||
<div class="detail_header">
|
|
||||||
<a-affix :target="(containerRef as HTMLElement)">
|
|
||||||
<a-page-header title="通知公告" :subtitle="title" @back="onBack">
|
|
||||||
<template #extra>
|
|
||||||
<a-button type="primary" @click="save">
|
|
||||||
<template #icon>
|
|
||||||
<icon-save v-if="isUpdate" />
|
|
||||||
<icon-send v-else />
|
|
||||||
</template>
|
|
||||||
<template #default>
|
|
||||||
{{ isUpdate ? '保存' : '发布' }}
|
|
||||||
</template>
|
|
||||||
</a-button>
|
|
||||||
</template>
|
|
||||||
</a-page-header>
|
|
||||||
</a-affix>
|
|
||||||
</div>
|
|
||||||
<div class="detail_content" style="display: flex; flex-direction: column;">
|
|
||||||
<GiForm ref="formRef" v-model="form" :columns="columns">
|
|
||||||
<template #noticeUsers>
|
|
||||||
<a-select
|
|
||||||
v-model="form.noticeUsers"
|
|
||||||
:options="userList"
|
|
||||||
multiple
|
|
||||||
:max-tag-count="4"
|
|
||||||
:allow-clear="true"
|
|
||||||
/>
|
|
||||||
<a-tooltip content="选择用户">
|
|
||||||
<a-button @click="onOpenSelectUser">
|
|
||||||
<template #icon>
|
|
||||||
<icon-plus />
|
|
||||||
</template>
|
|
||||||
</a-button>
|
|
||||||
</a-tooltip>
|
|
||||||
</template>
|
|
||||||
</GiForm>
|
|
||||||
<div style="flex: 1;">
|
|
||||||
<AiEditor v-model="form.content" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<a-modal
|
|
||||||
v-model:visible="visible"
|
|
||||||
title="用户选择"
|
|
||||||
:mask-closable="false"
|
|
||||||
:esc-to-close="false"
|
|
||||||
:width="width >= 1100 ? 1100 : '100%'"
|
|
||||||
draggable
|
|
||||||
@close="reset"
|
|
||||||
>
|
|
||||||
<UserSelect v-if="visible" ref="UserSelectRef" v-model:value="form.noticeUsers" @select-user="onSelectUser" />
|
|
||||||
</a-modal>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { Message } from '@arco-design/web-vue'
|
|
||||||
import { useWindowSize } from '@vueuse/core'
|
|
||||||
import AiEditor from './components/index.vue'
|
|
||||||
import { addNotice, getNotice, updateNotice } from '@/apis/system/notice'
|
|
||||||
import { listUserDict } from '@/apis'
|
|
||||||
import { type ColumnItem, GiForm } from '@/components/GiForm'
|
|
||||||
import type { LabelValueState } from '@/types/global'
|
|
||||||
import { useTabsStore } from '@/stores'
|
|
||||||
import { useResetReactive } from '@/hooks'
|
|
||||||
import { useDict } from '@/hooks/app'
|
|
||||||
|
|
||||||
const { width } = useWindowSize()
|
|
||||||
const route = useRoute()
|
|
||||||
const router = useRouter()
|
|
||||||
const tabsStore = useTabsStore()
|
|
||||||
|
|
||||||
const { id, type } = route.query
|
|
||||||
const isUpdate = computed(() => type === 'update')
|
|
||||||
const title = computed(() => (isUpdate.value ? '修改' : '新增'))
|
|
||||||
const containerRef = ref<HTMLElement | null>()
|
|
||||||
const formRef = ref<InstanceType<typeof GiForm>>()
|
|
||||||
const { notice_type } = useDict('notice_type')
|
|
||||||
|
|
||||||
const [form, resetForm] = useResetReactive({
|
|
||||||
title: '',
|
|
||||||
type: '',
|
|
||||||
effectiveTime: '',
|
|
||||||
terminateTime: '',
|
|
||||||
content: '',
|
|
||||||
noticeScope: 1,
|
|
||||||
})
|
|
||||||
|
|
||||||
const columns: ColumnItem[] = reactive([
|
|
||||||
{
|
|
||||||
label: '标题',
|
|
||||||
field: 'title',
|
|
||||||
type: 'input',
|
|
||||||
props: {
|
|
||||||
maxLength: 150,
|
|
||||||
showWordLimit: true,
|
|
||||||
},
|
|
||||||
rules: [{ required: true, message: '请输入标题' }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '类型',
|
|
||||||
field: 'type',
|
|
||||||
type: 'select',
|
|
||||||
props: {
|
|
||||||
options: notice_type,
|
|
||||||
},
|
|
||||||
rules: [{ required: true, message: '请输入类型' }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '生效时间',
|
|
||||||
field: 'effectiveTime',
|
|
||||||
type: 'date-picker',
|
|
||||||
props: {
|
|
||||||
showTime: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '终止时间',
|
|
||||||
field: 'terminateTime',
|
|
||||||
type: 'date-picker',
|
|
||||||
props: {
|
|
||||||
showTime: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '通知范围',
|
|
||||||
field: 'noticeScope',
|
|
||||||
type: 'radio-group',
|
|
||||||
props: {
|
|
||||||
options: [{ label: '所有人', value: 1 }, { label: '指定用户', value: 2 }],
|
|
||||||
},
|
|
||||||
rules: [{ required: true, message: '请选择通知范围' }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '指定用户',
|
|
||||||
field: 'noticeUsers',
|
|
||||||
type: 'select',
|
|
||||||
hide: () => {
|
|
||||||
return form.noticeScope === 1
|
|
||||||
},
|
|
||||||
rules: [{ required: true, message: '请选择指定用户' }],
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
// 修改
|
|
||||||
const onUpdate = async (id: string) => {
|
|
||||||
resetForm()
|
|
||||||
const res = await getNotice(id)
|
|
||||||
Object.assign(form, res.data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 返回
|
|
||||||
const onBack = () => {
|
|
||||||
router.back()
|
|
||||||
tabsStore.closeCurrent(route.path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保存
|
|
||||||
const save = async () => {
|
|
||||||
const isInvalid = await formRef.value?.formRef?.validate()
|
|
||||||
if (isInvalid) return false
|
|
||||||
try {
|
|
||||||
// 通知范围 所有人 去除指定用户
|
|
||||||
form.noticeUsers = form.noticeScope === 1 ? null : form.noticeUsers
|
|
||||||
if (isUpdate.value) {
|
|
||||||
await updateNotice(form, id as string)
|
|
||||||
Message.success('修改成功')
|
|
||||||
} else {
|
|
||||||
await addNotice(form)
|
|
||||||
Message.success('新增成功')
|
|
||||||
}
|
|
||||||
onBack()
|
|
||||||
return true
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 打开用户选择窗口
|
|
||||||
const visible = ref(false)
|
|
||||||
const onOpenSelectUser = () => {
|
|
||||||
visible.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
const UserSelectRef = ref()
|
|
||||||
// 重置
|
|
||||||
const reset = () => {
|
|
||||||
UserSelectRef.value?.onClearSelected()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 用户选择回调
|
|
||||||
const onSelectUser = (value: string[]) => {
|
|
||||||
form.noticeUsers = value
|
|
||||||
formRef.value?.formRef?.validateField('noticeUsers')
|
|
||||||
}
|
|
||||||
|
|
||||||
const userList = ref<LabelValueState[]>([])
|
|
||||||
onMounted(async () => {
|
|
||||||
// 当id存在与type为update时,执行修改操作
|
|
||||||
if (id && isUpdate.value) {
|
|
||||||
await onUpdate(id as string)
|
|
||||||
}
|
|
||||||
// 获取所有用户
|
|
||||||
const { data } = await listUserDict()
|
|
||||||
userList.value = data.map((item) => ({ ...item, value: `${item.value}` }))
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="less"></style>
|
|
||||||
@@ -1,123 +0,0 @@
|
|||||||
<!-- 未完善 -->
|
|
||||||
<template>
|
|
||||||
<div ref="divRef" class="container">
|
|
||||||
<div class="aie-container">
|
|
||||||
<div class="aie-header-panel" style="display: none;">
|
|
||||||
<div class="aie-container-header"></div>
|
|
||||||
</div>
|
|
||||||
<div class="aie-main">
|
|
||||||
<div class="aie-container-panel">
|
|
||||||
<div class="aie-container-main"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="aie-container-footer" style="display: none;"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { AiEditor, type AiEditorOptions } from 'aieditor'
|
|
||||||
import 'aieditor/dist/style.css'
|
|
||||||
import { useAppStore } from '@/stores'
|
|
||||||
|
|
||||||
defineOptions({ name: 'AiEditor' })
|
|
||||||
const props = defineProps<{
|
|
||||||
modelValue: string
|
|
||||||
options?: AiEditorOptions
|
|
||||||
}>()
|
|
||||||
const aieditor = ref<AiEditor | null>(null)
|
|
||||||
const appStore = useAppStore()
|
|
||||||
const divRef = ref<any>()
|
|
||||||
|
|
||||||
const editorConfig = reactive<AiEditorOptions>({
|
|
||||||
element: '',
|
|
||||||
theme: appStore.theme,
|
|
||||||
placeholder: '请输入内容',
|
|
||||||
content: '',
|
|
||||||
editable: false,
|
|
||||||
})
|
|
||||||
const init = () => {
|
|
||||||
aieditor.value?.destroy()
|
|
||||||
aieditor.value = new AiEditor(editorConfig)
|
|
||||||
}
|
|
||||||
watch(() => props.modelValue, (value) => {
|
|
||||||
if (value !== aieditor.value?.getHtml()) {
|
|
||||||
editorConfig.content = value
|
|
||||||
init()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
watch(() => appStore.theme, (value) => {
|
|
||||||
editorConfig.theme = value
|
|
||||||
init()
|
|
||||||
})
|
|
||||||
|
|
||||||
// 挂载阶段
|
|
||||||
onMounted(() => {
|
|
||||||
editorConfig.element = divRef.value
|
|
||||||
init()
|
|
||||||
})
|
|
||||||
// 销毁阶段
|
|
||||||
onUnmounted(() => {
|
|
||||||
aieditor.value?.destroy()
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.container {
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aie-header-panel {
|
|
||||||
position: sticky;
|
|
||||||
// top: 51px;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aie-header-panel aie-header>div {
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 10px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aie-container {
|
|
||||||
border: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aie-container-panel {
|
|
||||||
width: calc(100% - 2rem - 2px);
|
|
||||||
max-width: 826.77px;
|
|
||||||
margin: 0rem auto;
|
|
||||||
border: 1px solid var(--color-border-1);
|
|
||||||
background-color: var() rgba($color: var(--color-bg-1), $alpha: 1.0);
|
|
||||||
height: 100%;
|
|
||||||
padding: 1rem;
|
|
||||||
z-index: 99;
|
|
||||||
overflow: auto;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aie-main {
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
flex: 1;
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 1rem 0px;
|
|
||||||
background-color: var(--color-bg-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.aie-directory {
|
|
||||||
position: absolute;
|
|
||||||
top: 30px;
|
|
||||||
left: 10px;
|
|
||||||
width: 260px;
|
|
||||||
z-index: 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.aie-title1 {
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div ref="containerRef" class="detail">
|
|
||||||
<div class="detail_header">
|
|
||||||
<a-affix :target="(containerRef as HTMLElement)">
|
|
||||||
<a-page-header title="通知公告" subtitle="查看" @back="onBack">
|
|
||||||
</a-page-header>
|
|
||||||
</a-affix>
|
|
||||||
</div>
|
|
||||||
<div class="detail_content">
|
|
||||||
<h1 class="title">{{ form?.title }}</h1>
|
|
||||||
<div class="info">
|
|
||||||
<a-space>
|
|
||||||
<span>
|
|
||||||
<icon-user class="icon" />
|
|
||||||
<span class="label">发布人:</span>
|
|
||||||
<span>{{ form?.createUserString }}</span>
|
|
||||||
</span>
|
|
||||||
<a-divider direction="vertical" />
|
|
||||||
<span>
|
|
||||||
<icon-history class="icon" />
|
|
||||||
<span class="label">发布时间:</span>
|
|
||||||
<span>{{ form?.effectiveTime ? form?.effectiveTime : form?.createTime
|
|
||||||
}}</span>
|
|
||||||
</span>
|
|
||||||
<a-divider v-if="form?.updateTime" direction="vertical" />
|
|
||||||
<span v-if="form?.updateTime">
|
|
||||||
<icon-schedule class="icon" />
|
|
||||||
<span>更新时间:</span>
|
|
||||||
<span>{{ form?.updateTime }}</span>
|
|
||||||
</span>
|
|
||||||
</a-space>
|
|
||||||
</div>
|
|
||||||
<div style="flex: 1;">
|
|
||||||
<AiEditor v-model="form.content" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import AiEditor from './components/index.vue'
|
|
||||||
import { getNotice } from '@/apis/system/notice'
|
|
||||||
import { useTabsStore } from '@/stores'
|
|
||||||
import { useResetReactive } from '@/hooks'
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
const router = useRouter()
|
|
||||||
const tabsStore = useTabsStore()
|
|
||||||
|
|
||||||
const { id } = route.query
|
|
||||||
const containerRef = ref<HTMLElement | null>()
|
|
||||||
const [form, resetForm] = useResetReactive({
|
|
||||||
title: '',
|
|
||||||
createUserString: '',
|
|
||||||
effectiveTime: '',
|
|
||||||
createTime: '',
|
|
||||||
content: '',
|
|
||||||
})
|
|
||||||
|
|
||||||
// 回退
|
|
||||||
const onBack = () => {
|
|
||||||
router.back()
|
|
||||||
tabsStore.closeCurrent(route.path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 打开
|
|
||||||
const onOpen = async (id: string) => {
|
|
||||||
resetForm()
|
|
||||||
const { data } = await getNotice(id)
|
|
||||||
Object.assign(form, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
onOpen(id as string)
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.detail_content {
|
|
||||||
.title {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
margin-right: 3px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,133 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="gi_table_page">
|
|
||||||
<GiTable
|
|
||||||
row-key="id"
|
|
||||||
title=""
|
|
||||||
:data="dataList"
|
|
||||||
:columns="columns"
|
|
||||||
:loading="loading"
|
|
||||||
:scroll="{ x: '100%', y: '100%', minWidth: 1200 }"
|
|
||||||
:pagination="pagination"
|
|
||||||
:disabled-tools="['size']"
|
|
||||||
:disabled-column-keys="['title']"
|
|
||||||
@refresh="search"
|
|
||||||
>
|
|
||||||
<template #toolbar-left>
|
|
||||||
<a-input-search v-model="queryForm.title" placeholder="搜索标题" allow-clear @search="search" />
|
|
||||||
<a-select
|
|
||||||
v-model="queryForm.type"
|
|
||||||
:options="notice_type"
|
|
||||||
placeholder="请选择类型"
|
|
||||||
allow-clear
|
|
||||||
style="width: 150px"
|
|
||||||
@change="search"
|
|
||||||
/>
|
|
||||||
<a-button @click="reset">
|
|
||||||
<template #icon><icon-refresh /></template>
|
|
||||||
<template #default>重置</template>
|
|
||||||
</a-button>
|
|
||||||
</template>
|
|
||||||
<template #toolbar-right>
|
|
||||||
<a-button v-permission="['system:notice:add']" type="primary" @click="onAdd">
|
|
||||||
<template #icon><icon-plus /></template>
|
|
||||||
<template #default>新增</template>
|
|
||||||
</a-button>
|
|
||||||
</template>
|
|
||||||
<template #type="{ record }">
|
|
||||||
<GiCellTag :value="record.type" :dict="notice_type" />
|
|
||||||
</template>
|
|
||||||
<template #status="{ record }">
|
|
||||||
<GiCellTag :value="record.status" :dict="notice_status_enum" />
|
|
||||||
</template>
|
|
||||||
<template #action="{ record }">
|
|
||||||
<a-space>
|
|
||||||
<a-link v-permission="['system:notice:detail']" title="详情" @click="onDetail(record)">详情</a-link>
|
|
||||||
<a-link v-permission="['system:notice:update']" title="修改" @click="onUpdate(record)">修改</a-link>
|
|
||||||
<a-link v-permission="['system:notice:delete']" status="danger" title="删除" @click="onDelete(record)"> 删除 </a-link>
|
|
||||||
</a-space>
|
|
||||||
</template>
|
|
||||||
</GiTable>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { type NoticeQuery, type NoticeResp, deleteNotice, listNotice } from '@/apis/system'
|
|
||||||
import type { TableInstanceColumns } from '@/components/GiTable/type'
|
|
||||||
import { useTable } from '@/hooks'
|
|
||||||
import { useDict } from '@/hooks/app'
|
|
||||||
import { isMobile } from '@/utils'
|
|
||||||
import has from '@/utils/has'
|
|
||||||
|
|
||||||
defineOptions({ name: 'SystemNotice' })
|
|
||||||
|
|
||||||
const { notice_type, notice_status_enum } = useDict('notice_type', 'notice_status_enum')
|
|
||||||
|
|
||||||
const router = useRouter()
|
|
||||||
const queryForm = reactive<NoticeQuery>({
|
|
||||||
sort: ['id,desc'],
|
|
||||||
})
|
|
||||||
|
|
||||||
const {
|
|
||||||
tableData: dataList,
|
|
||||||
loading,
|
|
||||||
pagination,
|
|
||||||
search,
|
|
||||||
handleDelete,
|
|
||||||
} = useTable((page) => listNotice({ ...queryForm, ...page }), { immediate: true })
|
|
||||||
const columns: TableInstanceColumns[] = [
|
|
||||||
{
|
|
||||||
title: '序号',
|
|
||||||
width: 66,
|
|
||||||
align: 'center',
|
|
||||||
render: ({ rowIndex }) => h('span', {}, rowIndex + 1 + (pagination.current - 1) * pagination.pageSize),
|
|
||||||
},
|
|
||||||
{ title: '标题', dataIndex: 'title', slotName: 'title', minWidth: 200, ellipsis: true, tooltip: true },
|
|
||||||
{ title: '类型', dataIndex: 'type', slotName: 'type', align: 'center' },
|
|
||||||
{ title: '状态', dataIndex: 'status', slotName: 'status', align: 'center' },
|
|
||||||
{ title: '生效时间', dataIndex: 'effectiveTime', width: 180 },
|
|
||||||
{ title: '终止时间', dataIndex: 'terminateTime', width: 180 },
|
|
||||||
{ title: '创建人', dataIndex: 'createUserString', show: false, ellipsis: true, tooltip: true },
|
|
||||||
{ title: '创建时间', dataIndex: 'createTime', width: 180 },
|
|
||||||
{
|
|
||||||
title: '操作',
|
|
||||||
dataIndex: 'action',
|
|
||||||
slotName: 'action',
|
|
||||||
width: 160,
|
|
||||||
align: 'center',
|
|
||||||
fixed: !isMobile() ? 'right' : undefined,
|
|
||||||
show: has.hasPermOr(['system:notice:detail', 'system:notice:update', 'system:notice:delete']),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
// 重置
|
|
||||||
const reset = () => {
|
|
||||||
queryForm.title = undefined
|
|
||||||
queryForm.type = undefined
|
|
||||||
search()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除
|
|
||||||
const onDelete = (record: NoticeResp) => {
|
|
||||||
return handleDelete(() => deleteNotice(record.id), {
|
|
||||||
content: `是否确定删除公告「${record.title}」?`,
|
|
||||||
showModal: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 新增
|
|
||||||
const onAdd = () => {
|
|
||||||
router.push({ path: '/system/notice/add' })
|
|
||||||
}
|
|
||||||
|
|
||||||
// 修改
|
|
||||||
const onUpdate = (record: NoticeResp) => {
|
|
||||||
router.push({ path: '/system/notice/add', query: { id: record.id, type: 'update' } })
|
|
||||||
}
|
|
||||||
|
|
||||||
// 详情
|
|
||||||
const onDetail = (record: NoticeResp) => {
|
|
||||||
router.push({ path: '/system/notice/detail', query: { id: record.id } })
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
|
||||||
Reference in New Issue
Block a user