mirror of
https://github.com/continew-org/continew-admin-ui.git
synced 2025-10-24 18:57:11 +08:00
feat: 新增单页面通知公告编辑与查看
This commit is contained in:
@@ -1,10 +1,11 @@
|
|||||||
# 环境变量 (命名必须以 VITE_ 开头)
|
# 环境变量 (命名必须以 VITE_ 开头)
|
||||||
|
|
||||||
# 接口前缀
|
# 接口前缀
|
||||||
VITE_API_PREFIX = '/api'
|
VITE_API_PREFIX = '/api'
|
||||||
|
|
||||||
# 接口地址
|
# 接口地址
|
||||||
VITE_API_BASE_URL = 'http://localhost:8000'
|
VITE_API_BASE_URL = 'http://localhost:8000'
|
||||||
|
|
||||||
|
# 接口地址 (WebSocket)
|
||||||
VITE_API_WS_URL = 'ws://localhost:8000'
|
VITE_API_WS_URL = 'ws://localhost:8000'
|
||||||
|
|
||||||
# 地址前缀
|
# 地址前缀
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
"@vueuse/core": "^10.5.0",
|
"@vueuse/core": "^10.5.0",
|
||||||
"@wangeditor/editor": "^5.1.1",
|
"@wangeditor/editor": "^5.1.1",
|
||||||
"@wangeditor/editor-for-vue": "^5.1.12",
|
"@wangeditor/editor-for-vue": "^5.1.12",
|
||||||
|
"aieditor": "^1.0.13",
|
||||||
"animate.css": "^4.1.1",
|
"animate.css": "^4.1.1",
|
||||||
"axios": "^0.27.2",
|
"axios": "^0.27.2",
|
||||||
"codemirror": "^6.0.1",
|
"codemirror": "^6.0.1",
|
||||||
@@ -82,7 +83,7 @@
|
|||||||
"vue-tsc": "^2.0.6"
|
"vue-tsc": "^2.0.6"
|
||||||
},
|
},
|
||||||
"simple-git-hooks": {
|
"simple-git-hooks": {
|
||||||
"pre-commit": "npm lint-staged"
|
"pre-commit": "pnpm lint-staged"
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"*": "eslint --fix"
|
"*": "eslint --fix"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div v-if="isDesktop" class="asider" :class="{ 'app-menu-dark': appStore.menuDark }"
|
<div v-if="isDesktop" class="asider" :class="{ 'app-menu-dark': appStore.menuDark }"
|
||||||
:style="appStore.menuDark ? appStore.themeCSSVar : undefined">
|
:style="appStore.menuDark ? appStore.themeCSSVar : undefined">
|
||||||
<Logo :collapsed="appStore.menuCollapse"></Logo>
|
<Logo :collapsed="appStore.menuCollapse"></Logo>
|
||||||
<a-layout-sider class="menu" collapsible breakpoint="xl" hide-trigger width="auto"
|
<a-layout-sider class="menu" collapsible breakpoint="xl" hide-trigger style="width: auto;"
|
||||||
:collapsed="appStore.menuCollapse" @collapse="handleCollapse">
|
:collapsed="appStore.menuCollapse" @collapse="handleCollapse">
|
||||||
<a-scrollbar outer-class="h-full" style="height: 100%; overflow: auto">
|
<a-scrollbar outer-class="h-full" style="height: 100%; overflow: auto">
|
||||||
<Menu></Menu>
|
<Menu></Menu>
|
||||||
|
|||||||
@@ -1,6 +1,22 @@
|
|||||||
/* 全局样式 */
|
/* 全局样式 */
|
||||||
@import './var.scss';
|
@import './var.scss';
|
||||||
|
// 全局滚动条
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background-color: var(--color-bg-3);
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
background-color: var(--color-bg-3);
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width:8px;
|
||||||
|
height: 5px;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-track-piece {
|
||||||
|
background-color: rgba(0, 0, 0, 0);
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
// 通用外边距
|
// 通用外边距
|
||||||
.gi_margin {
|
.gi_margin {
|
||||||
margin: $margin;
|
margin: $margin;
|
||||||
@@ -228,7 +244,25 @@
|
|||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
.detail{
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
&_header{
|
||||||
|
background: var(--color-bg-1);
|
||||||
|
}
|
||||||
|
&_content{
|
||||||
|
position: relative;
|
||||||
|
flex: 1;
|
||||||
|
// height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: $padding;
|
||||||
|
margin: $margin;
|
||||||
|
background: var(--color-bg-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
.gi_card_title {
|
.gi_card_title {
|
||||||
.arco-card-header-title::before {
|
.arco-card-header-title::before {
|
||||||
content: '';
|
content: '';
|
||||||
|
|||||||
123
src/views/system/notice/components/detail/index.vue
Normal file
123
src/views/system/notice/components/detail/index.vue
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
<!-- 未完善 -->
|
||||||
|
<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 lang="scss" scoped>
|
||||||
|
.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>
|
||||||
209
src/views/system/notice/components/edit/index.vue
Normal file
209
src/views/system/notice/components/edit/index.vue
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
<!-- 未完善 -->
|
||||||
|
<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 lang="scss" scoped>
|
||||||
|
.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>
|
||||||
@@ -67,6 +67,7 @@ defineOptions({ name: 'SystemNotice' })
|
|||||||
|
|
||||||
const { notice_type, notice_status_enum } = useDict('notice_type', 'notice_status_enum')
|
const { notice_type, notice_status_enum } = useDict('notice_type', 'notice_status_enum')
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
const queryForm = reactive<NoticeQuery>({
|
const queryForm = reactive<NoticeQuery>({
|
||||||
sort: ['createTime,desc']
|
sort: ['createTime,desc']
|
||||||
})
|
})
|
||||||
@@ -121,18 +122,21 @@ const onDelete = (record: NoticeResp) => {
|
|||||||
const NoticeAddModalRef = ref<InstanceType<typeof NoticeAddModal>>()
|
const NoticeAddModalRef = ref<InstanceType<typeof NoticeAddModal>>()
|
||||||
// 新增
|
// 新增
|
||||||
const onAdd = () => {
|
const onAdd = () => {
|
||||||
NoticeAddModalRef.value?.onAdd()
|
// NoticeAddModalRef.value?.onAdd()
|
||||||
|
router.push({ path: '/system/notice/add' })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改
|
// 修改
|
||||||
const onUpdate = (record: NoticeResp) => {
|
const onUpdate = (record: NoticeResp) => {
|
||||||
NoticeAddModalRef.value?.onUpdate(record.id)
|
// NoticeAddModalRef.value?.onUpdate(record.id)
|
||||||
|
router.push({ path: '/system/notice/add', query: { id: record.id, type: 'edit' } })
|
||||||
}
|
}
|
||||||
|
|
||||||
const NoticeDetailModalRef = ref<InstanceType<typeof NoticeDetailModal>>()
|
const NoticeDetailModalRef = ref<InstanceType<typeof NoticeDetailModal>>()
|
||||||
// 详情
|
// 详情
|
||||||
const onDetail = (record: NoticeResp) => {
|
const onDetail = (record: NoticeResp) => {
|
||||||
NoticeDetailModalRef.value?.onDetail(record.id)
|
// NoticeDetailModalRef.value?.onDetail(record.id)
|
||||||
|
router.push({ path: '/system/notice/detail', query: { id: record.id } })
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
116
src/views/system/notice/page/add.vue
Normal file
116
src/views/system/notice/page/add.vue
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
<template>
|
||||||
|
<div ref="containerRef" class="detail">
|
||||||
|
<div class="detail_header">
|
||||||
|
<a-affix :target="(containerRef as HTMLElement)">
|
||||||
|
<a-page-header title="通知公告" :subtitle="type === 'edit' ? '修改' : '新增'" @back="onBack">
|
||||||
|
<template #extra>
|
||||||
|
<a-button type="primary" @click="onReleased">{{ type === 'edit' ? '修改' : '发布' }}</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" :options="options" :columns="columns" />
|
||||||
|
<div style="flex: 1;">
|
||||||
|
<AiEditor v-model="form.content" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="tsx">
|
||||||
|
import { Message } from '@arco-design/web-vue'
|
||||||
|
import AiEditor from '../components/edit/index.vue'
|
||||||
|
import { useTabsStore } from '@/stores'
|
||||||
|
import { type Columns, GiForm, type Options } from '@/components/GiForm'
|
||||||
|
import { addNotice, getNotice, updateNotice } from '@/apis'
|
||||||
|
import { useForm } from '@/hooks'
|
||||||
|
import { useDict } from '@/hooks/app'
|
||||||
|
|
||||||
|
const { notice_type } = useDict('notice_type')
|
||||||
|
const containerRef = ref<HTMLElement | null>()
|
||||||
|
const tabsStore = useTabsStore()
|
||||||
|
const route = useRoute()
|
||||||
|
const formRef = ref<InstanceType<typeof GiForm>>()
|
||||||
|
const { id, type } = route.query
|
||||||
|
const { form, resetForm } = useForm({
|
||||||
|
title: '',
|
||||||
|
type: '',
|
||||||
|
effectiveTime: '',
|
||||||
|
terminateTime: '',
|
||||||
|
content: ''
|
||||||
|
})
|
||||||
|
const options: Options = {
|
||||||
|
form: {},
|
||||||
|
col: { xs: 24, sm: 24, md: 12, lg: 12, xl: 12, xxl: 12 },
|
||||||
|
btns: { hide: true }
|
||||||
|
}
|
||||||
|
|
||||||
|
const columns: Columns = reactive([
|
||||||
|
{
|
||||||
|
label: '标题',
|
||||||
|
field: 'title',
|
||||||
|
type: 'input',
|
||||||
|
rules: [{ required: true, message: '请输入标题' }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '类型',
|
||||||
|
field: 'type',
|
||||||
|
type: 'select',
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
])
|
||||||
|
// 修改
|
||||||
|
const onUpdate = async (id: string) => {
|
||||||
|
resetForm()
|
||||||
|
const res = await getNotice(id)
|
||||||
|
Object.assign(form, res.data)
|
||||||
|
}
|
||||||
|
const onBack = () => {
|
||||||
|
tabsStore.closeCurrent(route.path)
|
||||||
|
}
|
||||||
|
const onReleased = async () => {
|
||||||
|
const isInvalid = await formRef.value?.formRef?.validate()
|
||||||
|
if (isInvalid) return false
|
||||||
|
try {
|
||||||
|
if (type === 'edit') {
|
||||||
|
await updateNotice(form, id as string)
|
||||||
|
Message.success('修改成功')
|
||||||
|
} else {
|
||||||
|
await addNotice(form)
|
||||||
|
Message.success('新增成功')
|
||||||
|
}
|
||||||
|
onBack()
|
||||||
|
return true
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
// 当id存在与type为edit时,执行修改操作
|
||||||
|
if (id && type === 'edit') {
|
||||||
|
onUpdate(id as string)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less"></style>
|
||||||
78
src/views/system/notice/page/detail.vue
Normal file
78
src/views/system/notice/page/detail.vue
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
<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" style="display: flex; flex-direction: column;">
|
||||||
|
<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-space>
|
||||||
|
</div>
|
||||||
|
<div style="flex: 1;">
|
||||||
|
<AiEditor v-model="form.content" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="tsx">
|
||||||
|
import AiEditor from '../components/detail/index.vue'
|
||||||
|
import { useTabsStore } from '@/stores'
|
||||||
|
import { getNotice } from '@/apis'
|
||||||
|
import { useForm } from '@/hooks'
|
||||||
|
|
||||||
|
const containerRef = ref<HTMLElement | null>()
|
||||||
|
const tabsStore = useTabsStore()
|
||||||
|
const route = useRoute()
|
||||||
|
const { id } = route.query
|
||||||
|
const { form, resetForm } = useForm({
|
||||||
|
title: '',
|
||||||
|
createUserString: '',
|
||||||
|
effectiveTime: '',
|
||||||
|
createTime: '',
|
||||||
|
content: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// 修改
|
||||||
|
const onDetail = async (id: string) => {
|
||||||
|
resetForm()
|
||||||
|
const res = await getNotice(id)
|
||||||
|
Object.assign(form, res.data)
|
||||||
|
}
|
||||||
|
const onBack = () => {
|
||||||
|
tabsStore.closeCurrent(route.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
onDetail(id as string)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.detail_content {
|
||||||
|
.title {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
text-align: right;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user