Merge branch 'zhangtao' of zhangtao/hhz_restructure into develop

This commit is contained in:
zhangtao 2022-11-21 17:23:09 +08:00
commit 2c5bcfd9e5
12 changed files with 643 additions and 20 deletions

2
.gitignore vendored
View File

@ -2,4 +2,4 @@ node_modules
.DS_Store
dist
dist-ssr
*.local
*.localhost

View File

@ -45,4 +45,3 @@ export function advertisingDelete(ids) {
method: 'delete'
})
}

71
src/api/setting/notice.js Normal file
View File

@ -0,0 +1,71 @@
import { defAxios as request } from '@/utils/http'
/**
* @description: 分页查询广告列表
* @return {*}
*/
export function fetchNoticeList(params) {
return request({
url: '/notice/index',
method: 'get',
params
})
}
/**
* @description: 创建广告
* @return {*}
*/
export function noticeCreate(data) {
return request({
url: '/notice/add',
method: 'post',
data
})
}
/**
* @description: 更新通知公告
* @return {*}
*/
export function noticeUpdate(data) {
return request({
url: `/notice/edit`,
method: 'put',
data
})
}
/**
* @description: 更新通知公告
* @return {*}
*/
export function isTopUpdate(data) {
return request({
url: `/notice/setIsTop`,
method: 'put',
data
})
}
/**
* @description: 更新通知公告
* @return {*}
*/
export function statusUpdate(data) {
return request({
url: `/notice/status`,
method: 'put',
data
})
}
/**
* @description: 删除广告
* @return {*}
*/
export function noticeDelete(ids) {
return request({
url: `/notice/delete/${ids}`,
method: 'delete'
})
}

View File

@ -45,3 +45,8 @@ export const ADVERTISING_STATUS = [
{ label: '正常', value: 1, color: { color: '#e6f7ff', textColor: '#096dd9', borderColor: '#91d5ff' }},
{ label: '停用', value: 2, color: { color: '#fff1f0', textColor: '#cf1322', borderColor: '#ffa39e' }}
]
export const NOTICE_SOURCE = [
{ label: '内部通知', value: 1, color: { color: '#f6ffed', textColor: '#389e0d', borderColor: '#b7eb8f' }},
{ label: '外部新闻', value: 2, color: { color: '#fff7e6', textColor: '#d46b08', borderColor: '#ffd591' }}
]

View File

@ -28,7 +28,7 @@
</n-radio-group>
<n-date-picker v-if="item.type === 'date'" v-model:formatted-value="advertisingForm[item.key]" v-bind="item.props" />
<n-input-number v-if="item.type === 'number'" v-model:value="advertisingForm[item.key]" v-bind="item.props" />
<TinymceEditor v-if="item.type === 'editor'" v-model:modelValue="advertisingForm[item.key]" v-bind="item.props" />
<TinymceEditor v-if="item.type === 'editor'" v-model:modelValue="advertisingForm[item.key]" :disabled="disabled" v-bind="item.props" />
</n-form-item>
</template>
</div>

View File

@ -22,7 +22,7 @@ export const form = reactive({
sort: [{ required: true, type: 'number', message: '请输入排序号', trigger: 'blur' }]
},
formItem: [
{ type: 'oss', refIndex: 0, key: 'imageStatus', file: 'cover', label: '广告图片', props: { maxlength: '20', placeholder: '请输入部门编号', clearable: true }},
{ type: 'oss', refIndex: 0, key: 'imageStatus', file: 'cover', label: '广告图片' },
{ type: 'input', key: 'title', label: '广告标题', props: { maxlength: '20', placeholder: '请输入广告标题', clearable: true }},
{ type: 'input', key: 'description', label: '广告描述', props: { maxlength: '20', placeholder: '请输入广告描述', clearable: true }},
@ -31,7 +31,7 @@ export const form = reactive({
{ type: 'select', key: 'platform', label: '投放平台', props: { options: ADVERTISING_PLATFORM, placeholder: '请选择投放平台' }},
{ type: 'input', key: 'url', label: '广告URL', props: { maxlength: '20', placeholder: '请输入广告URL', clearable: true }},
{ type: 'radio', key: 'status', label: '广告状态', options: ADVERTISING_STATUS, mode: ['config'] },
{ type: 'radio', key: 'status', label: '广告状态', options: ADVERTISING_STATUS },
{ type: 'number', key: 'width', label: '广告宽度', props: { min: 0, placeholder: '请输入广告宽度', showButton: false, clearable: true }},
{ type: 'number', key: 'height', label: '广告高度', props: { min: 0, placeholder: '请输入广告高度', showButton: false, clearable: true }},

View File

@ -193,16 +193,16 @@ const data = reactive({
render(row) {
return h(TableAction, {
actions: [
{
label: '添加',
type: 'button',
props: {
type: 'primary',
text: true,
onClick: getRowData.bind(null, row, 'create')
},
auth: 'basic_list'
},
// {
// label: '查看',
// type: 'button',
// props: {
// type: 'primary',
// text: true,
// onClick: getRowData.bind(null, row, 'preview')
// },
// auth: 'basic_list'
// },
{
label: '修改',
type: 'button',

View File

@ -0,0 +1,187 @@
<template>
<Modal
:options="getModalOptions"
:on-positive-click="handleConfirm"
:on-negative-click="handleClose"
:on-close="handleClose"
>
<template #Context>
<n-form
ref="formRef"
:model="noticeForm"
:rules="noticeRules"
:label-width="100"
label-placement="left"
require-mark-placement="left"
:disabled="disabled"
>
<div class="flex__body">
<template v-for="(item,index) in getFormOptions" :key="index">
<n-form-item :label="item.label" :path="item.key" :class="{ 'flex__item': !['oss','editor'].includes(item.type) }">
<UploadOss v-if="item.type === 'oss'" :ref="el=>{ossRefs[item.refIndex] = el}" :default-list="noticeForm[item.file]" @upload-status="handleUploadStatus" />
<n-input v-if="item.type === 'input'" v-model:value="noticeForm[item.key]" v-bind="item.props" />
<n-select v-if="item.type === 'select'" v-model:value="noticeForm[item.key]" v-bind="item.props" />
<n-radio-group v-if="item.type === 'radio'" v-model:value="noticeForm[item.key]" :name="item.key">
<n-space>
<n-radio v-for="(cItem,cIndex) in item.options" :key="`${item.key}_${cIndex}`" :value="cItem.value"> {{ cItem.label }}</n-radio>
</n-space>
</n-radio-group>
<TinymceEditor v-if="item.type === 'editor'" v-model:modelValue="noticeForm[item.key]" :disabled="disabled" v-bind="item.props" />
</n-form-item>
</template>
</div>
</n-form>
</template>
</Modal>
</template>
<script>
import { form } from '../tools/form.js'
import Modal from '@/components/Modal/index.vue'
import UploadOss from '@/components/UploadOss/index.vue'
import TinymceEditor from '@/components/TinymceEditor/index.vue'
import { noticeCreate, noticeUpdate } from '@/api/setting/notice.js'
import { defineComponent, ref, reactive, computed, toRefs } from 'vue'
export default defineComponent({
name: 'UserModal',
components: { Modal, UploadOss, TinymceEditor },
props: {
visible: {
type: Boolean,
default: false
},
type: {
type: String,
default: 'create'
},
data: {
type: Object,
default: () => {}
}
},
emits: {
'update:visible': null,
'reload': null
},
setup(props, { emit }) {
const MODAL_TYPE = {
'create': '新建广告',
'preview': '广告详情',
'update': '编辑广告'
}
const { noticeForm, noticeRules } = form
const formRef = ref()
const ossRefs = ref([])
const data = reactive({
noticeForm: {
...noticeForm,
imageStatus: '',
...props.data
},
noticeRules: {
...noticeRules
},
disabled: props.type === 'preview'
})
const getModalOptions = computed(() => {
return {
title: MODAL_TYPE[props.type],
width: 800,
show: props.visible,
trapFocus: false,
negativeText: '取消',
positiveText: '确认'
}
})
const getFormOptions = computed(() => {
return {
...form.formItem
}
})
function handleConfirm() {
formRef.value.validate((errors) => {
if (!errors) {
const uploads = ossRefs.value.map((item, index) => {
return item.startUpload()
})
Promise.all(uploads)
.then(response => {
const isError = response.map((item) => {
return item.includes('error')
})
if (!isError.includes(true)) {
const imageStr = response.join()
const params = { ...data.noticeForm, cover: imageStr }
if (params.id) {
noticeUpdate(params).then(res => {
if (res.code === 0) {
emit('reload')
handleClose()
}
}).catch(e => {
console.log(e)
})
} else {
noticeCreate(params).then(res => {
if (res.code === 0) {
emit('reload')
handleClose()
}
})
}
} else {
$message.error('文件上传失败,请稍后重试')
}
})
}
})
}
/* 关闭弹窗 */
const handleClose = () => {
emit('update:visible', false)
}
function handleUploadStatus(status) {
data.noticeForm.imageStatus = status
}
return {
...toRefs(data),
formRef,
ossRefs,
getModalOptions,
getFormOptions,
handleConfirm,
handleClose,
handleUploadStatus
}
}
})
</script>
<style scoped lang='scss'>
.n-input-number{
width: 100%;
}
.flex__body{
display: flex;
width: 100%;
flex-wrap: wrap;
.n-form-item{
width: 100%;
}
.flex__item{
width: 50%;
}
}
.n-date-picker{
width: 100%;
}
</style>

View File

@ -1,17 +1,99 @@
<template>
<div>
消息通知
<n-card>
<headSearch :info="search" @search="handleSearch" @reset="handleSearch" />
<data-table
ref="tableRef"
:columns="columns"
:request="loadDataTable"
:row-key="(row) => row.id"
:scroll-x="1700"
size="large"
@update:checked-row-keys="handleRowCheck"
>
<template #tableTitle>
<n-button type="primary" @click="handleModal"> 新建 </n-button>
<n-popconfirm
negative-text="取消"
positive-text="确认"
@positive-click="handleRowDelete"
>
<template #trigger>
<n-button type="primary">删除</n-button>
</template>
确定删除选中的数据吗?
</n-popconfirm>
</template>
</data-table>
</n-card>
</div>
<!-- 新增编辑弹窗 -->
<NoticeModal v-if="modalShow" v-model:visible="modalShow" :data="rowData" :type="modalType" @reload="handleSearch" />
</template>
<script>
export default {
name: 'NoticePage',
setup() {
import search from './tools/search.js'
import table from './tools/table.js'
import headSearch from '@/components/Search/index.vue'
import dataTable from '@/components/DataTable/index.vue'
import NoticeModal from './components/NoticeModal.vue'
import { fetchNoticeList } from '@/api/setting/notice.js'
import { ref, unref, toRefs, reactive, onUnmounted } from 'vue'
export default {
name: 'NotciePage',
components: { dataTable, NoticeModal, headSearch },
setup() {
const data = reactive({
...toRefs(table),
search
})
const loadDataTable = async(res) => {
const _params = {
...unref(data.searchParams),
...res
}
return await fetchNoticeList(_params)
}
//
function handleModal() {
data.rowData = null
data.modalType = 'create'
data.modalShow = true
}
const selectedIds = ref([])
function handleRowCheck(rowKeys) {
selectedIds.value = rowKeys
}
//
function handleRowDelete() {
if (selectedIds.value.length) {
data.deleteData(selectedIds.value)
} else {
$message.warning('请至少选中一条数据')
}
}
onUnmounted(() => {
data.searchParams = null
})
return {
...toRefs(data),
loadDataTable,
handleModal,
handleRowCheck,
handleRowDelete
}
}
}
</script>
<style scoped lang='scss'>
.n-button + .n-button {
margin-left: 10px;
}
</style>

View File

@ -0,0 +1,31 @@
import { reactive } from 'vue'
import { NOTICE_SOURCE, ADVERTISING_STATUS } from '@/utils/dictionary.js'
export const form = reactive({
noticeForm: {
image: null,
title: null,
source: 1,
status: 1,
url: null,
guide: null,
content: null
},
noticeRules: {
title: [{ required: true, message: '请输入广告标题', trigger: 'blur' }]
},
formItem: [
{ type: 'oss', refIndex: 0, key: 'imageStatus', file: 'image', label: '公告图片' },
{ type: 'input', key: 'title', label: '通知标题', props: { maxlength: '20', placeholder: '请输入通知标题', clearable: true }},
{ type: 'select', key: 'source', label: '通知来源', props: { options: NOTICE_SOURCE, placeholder: '请选择通知来源' }},
{ type: 'radio', key: 'status', label: '通知状态', options: ADVERTISING_STATUS },
{ type: 'input', key: 'url', label: '外部地址', props: { maxlength: '20', placeholder: '请输入外部地址', clearable: true }},
{ type: 'input', key: 'guide', label: '公告摘要', props: { maxlength: '20', placeholder: '请输入公告摘要', clearable: true }},
{ type: 'editor', key: 'content', label: '通知内容', props: { height: 300 }}
]
})

View File

@ -0,0 +1,14 @@
import { reactive } from 'vue'
const data = reactive([
{
label: '通知标题',
key: 'title',
props: {
placeholder: '请输入通知标题'
}
}
])
export default data

View File

@ -0,0 +1,234 @@
import { h, ref, reactive } from 'vue'
import TableImage from '@/components/DataTable/tools/Image.vue'
import TableTags from '@/components/DataTable/tools/Tags.vue'
import TableSwitch from '@/components/DataTable/tools/Switch.vue'
import TableAction from '@/components/DataTable/tools/Action.vue'
import { NOTICE_SOURCE } from '@/utils/dictionary.js'
import { noticeDelete, isTopUpdate, statusUpdate } from '@/api/setting/notice.js'
/* 注册table */
const tableRef = ref()
const searchParams = ref()
function handleSearch(params) {
searchParams.value = { ...params }
tableRef.value.reFetch({ searchParams })
}
/**
* @description: 获取数据及操作
* @param {*} row 单行数据
* @param {*} type 操作类型 create:创建preview:预览edit:编辑
* @return {*}
*/
function getRowData(row, type) {
data.rowData = type === 'create' ? { pid: row.id } : row
data.modalType = type
data.modalShow = true
}
// 删除方法
function deleteData(id) {
noticeDelete(id)
.then((res) => {
if (res.code === 0) {
handleSearch()
}
})
.catch((e) => {
console.log(e)
})
}
/**
* @description: 改变状态
* @param {*} row 选中数据
* @return {*}
*/
function handleIsTopChange(row) {
isTopUpdate({ id: row.data.id, isTop: row.value })
.then((res) => {
if (res.code === 0) {
handleSearch()
}
})
.catch((e) => {
console.log(e)
})
}
/**
* @description: 改变状态
* @param {*} row 选中数据
* @return {*}
*/
function handleStatusChange(row) {
statusUpdate({ id: row.data.id, status: row.value })
.then((res) => {
if (res.code === 0) {
handleSearch()
}
})
.catch((e) => {
console.log(e)
})
}
const data = reactive({
tableRef,
searchParams,
rowData: {},
modalType: 'create',
modalShow: false,
handleSearch,
columns: [
{
type: 'selection'
},
{
title: '编号',
key: 'key',
render: (_, index) => {
return `${index + 1}`
},
align: 'center',
width: 50
},
{
title: '通知标题',
key: 'title',
align: 'center',
ellipsis: {
tooltip: true
},
width: 500
},
{
title: '公告图片',
key: 'image',
render(row) {
return h(TableImage, {
images: {
width: 36,
height: 36,
src: row.image
}
})
},
align: 'center',
width: 100
},
{
title: '通知来源',
key: 'source',
align: 'center',
width: 100,
render(row) {
return h(TableTags, {
data: row.source,
filters: NOTICE_SOURCE,
tags: {
bordered: true
}
})
}
},
{
title: '是否置顶',
key: 'isTop',
align: 'center',
width: 100,
render(row) {
return h(TableSwitch, {
data: { id: row.id, isTop: row.isTop },
rowKey: 'isTop',
checkedValue: 1,
uncheckedValue: 2,
onChange: handleIsTopChange.bind(row)
})
}
},
{
title: '状态',
key: 'status',
align: 'center',
width: 100,
render(row) {
return h(TableSwitch, {
data: { id: row.id, status: row.status },
rowKey: 'status',
checkedValue: 1,
uncheckedValue: 2,
onChange: handleStatusChange.bind(row)
})
}
},
{
title: '浏览量',
key: 'browse',
align: 'center',
width: 100
},
{
title: '创建时间',
key: 'createTime',
align: 'center',
width: 200
},
{
title: '更新时间',
key: 'updateTime',
align: 'center',
width: 200
},
{
title: '操作',
align: 'center',
width: 150,
fixed: 'right',
render(row) {
return h(TableAction, {
actions: [
// {
// label: '查看',
// type: 'button',
// props: {
// type: 'primary',
// text: true,
// onClick: getRowData.bind(null, row, 'preview')
// },
// auth: 'basic_list'
// },
{
label: '修改',
type: 'button',
props: {
type: 'primary',
text: true,
onClick: getRowData.bind(null, row, 'update')
},
auth: 'basic_list'
},
{
label: '删除',
type: 'popconfirm',
auth: 'basic_list',
tip: '确定删除这条数据吗?',
props: {
onPositiveClick: deleteData.bind(null, [row.id])
},
ButtonProps: {
text: true,
type: 'primary'
}
}
],
align: 'center'
})
}
}
]
})
export default data