Files
intc-single-ultra-app/src/pages_mine/pages/system/notice/addEdit.vue
2026-02-07 23:24:13 +08:00

576 lines
15 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="container" style="padding-bottom:1rpx;">
<u-navbar
leftIconSize="40rpx"
leftIconColor="#333333"
:title="title"
>
</u-navbar>
<view class="section">
<view class="section-title">公告信息</view>
<view class="form-view">
<u--form labelPosition="left" :model="form" ref="uForm" labelWidth="160rpx"
:labelStyle="{ color: '#333333', fontSize: '30rpx' }">
<u-form-item label="公告标题" prop="noticeTitle" required>
<u--input v-model="form.noticeTitle" placeholder="请输入公告标题"
inputAlign="left" :customStyle="getInputStyle('noticeTitle')"></u--input>
</u-form-item>
<u-form-item label="公告类型" prop="noticeType" required @click="showNoticeTypePicker = true" class="with-arrow">
<u--input v-model="noticeTypeName" disabled disabledColor="#ffffff" placeholder="请选择公告类型"
inputAlign="left" :customStyle="inputBaseStyle"></u--input>
</u-form-item>
<u-form-item label="状态" prop="status" @click="showStatusPicker = true" class="with-arrow">
<u--input v-model="statusName" disabled disabledColor="#ffffff" placeholder="请选择状态"
inputAlign="left" :customStyle="inputBaseStyle"></u--input>
</u-form-item>
<u-form-item label="内容" prop="noticeContent" labelPosition="top">
<view class="editor-container">
<view class="editor-toolbar">
<view class="toolbar-item" @click="format('bold')">
<text class="iconfont">B</text>
</view>
<view class="toolbar-item" @click="format('italic')">
<text class="iconfont italic">I</text>
</view>
<view class="toolbar-item" @click="format('underline')">
<text class="iconfont underline">U</text>
</view>
<view class="toolbar-divider"></view>
<view class="toolbar-item" @click="insertImage">
<uni-icons type="image" size="16" color="#333333"></uni-icons>
</view>
<view class="toolbar-divider"></view>
<view class="toolbar-item" @click="undo">
<text class="iconfont"></text>
</view>
<view class="toolbar-item" @click="redo">
<text class="iconfont"></text>
</view>
</view>
<editor
id="editor"
class="editor-content"
:placeholder="'请输入公告内容'"
@ready="onEditorReady"
@input="onEditorInput"
@focus="editorFocus"
@blur="editorBlur"
:show-img-size="true"
:show-img-toolbar="true"
:show-img-resize="true"
></editor>
<view class="editor-counter">
<text>{{ contentLength }}/20000</text>
</view>
</view>
</u-form-item>
</u--form>
<view class="form-btn">
<u-button type="primary" text="保存" @click="submitForm"></u-button>
</view>
</view>
</view>
<!-- 公告类型选择器 -->
<u-picker itemHeight="88" :show="showNoticeTypePicker" :columns="noticeTypeColumns" keyName="dictLabel"
@cancel="showNoticeTypePicker = false" @confirm="handleNoticeTypeConfirm"></u-picker>
<!-- 状态选择器 -->
<u-picker itemHeight="88" :show="showStatusPicker" :columns="statusColumns" keyName="dictLabel"
@cancel="showStatusPicker = false" @confirm="handleStatusConfirm"></u-picker>
</view>
</template>
<script setup>
import { ref, reactive, onMounted, nextTick } from 'vue'
import { getNotice, addNotice, updateNotice } from '@/api/system/notice'
import { getDicts } from "@/api/system/dict/data"
import { onLoad, onReady } from "@dcloudio/uni-app"
import modal from '@/plugins/modal'
const title = ref('新增公告')
const noticeId = ref(undefined)
const noticeTypeList = ref([])
const statusList = ref([])
const noticeTypeColumns = ref([])
const statusColumns = ref([])
const showNoticeTypePicker = ref(false)
const showStatusPicker = ref(false)
const noticeTypeName = ref('')
const statusName = ref('')
const editorCtx = ref(null)
const contentLength = ref(0)
const form = reactive({
noticeId: undefined,
noticeTitle: '',
noticeType: '',
noticeContent: '',
status: '0'
})
// 表单验证错误字段
const errorFields = ref([])
// 输入框基础样式
const inputBaseStyle = {
background: '#ffffff',
border: '2rpx solid #dcdfe6',
borderRadius: '8rpx',
padding: '0 24rpx',
height: '68rpx',
width: '100%',
boxSizing: 'border-box'
}
// 输入框错误样式
const inputErrorStyle = {
background: '#fef0f0',
border: '2rpx solid #f56c6c',
borderRadius: '8rpx',
padding: '0 24rpx',
height: '68rpx',
width: '100%',
boxSizing: 'border-box'
}
// 根据字段名获取输入框样式
const getInputStyle = (field) => {
return errorFields.value.includes(field) ? inputErrorStyle : inputBaseStyle
}
onLoad((options) => {
if (options.id) {
noticeId.value = options.id
title.value = '修改公告'
getNotice(noticeId.value).then(response => {
Object.assign(form, response.data)
// 更新显示名称
updateNoticeTypeName()
updateStatusName()
// 如果编辑器已经初始化,设置内容
if (editorCtx.value && form.noticeContent) {
editorCtx.value.setContents({
html: form.noticeContent
})
}
}).catch(error => {
console.error('获取公告详情失败:', error)
})
}
// 获取字典数据
getDicts('sys_notice_type').then(res => {
noticeTypeList.value = res.data
noticeTypeColumns.value = [res.data]
// 如果是新增模式,设置默认值
if (!noticeId.value && res.data.length > 0) {
form.noticeType = res.data[0].dictValue
noticeTypeName.value = res.data[0].dictLabel
}
}).catch(error => {
console.error('获取公告类型字典失败:', error)
})
getDicts('sys_notice_status').then(res => {
statusList.value = res.data
statusColumns.value = [res.data]
// 更新状态显示
updateStatusName()
}).catch(error => {
console.error('获取公告状态字典失败:', error)
})
})
// 更新公告类型显示名称
function updateNoticeTypeName() {
if (noticeTypeList.value.length > 0) {
const item = noticeTypeList.value.find(v => v.dictValue === form.noticeType)
noticeTypeName.value = item ? item.dictLabel : ''
}
}
// 更新状态显示名称
function updateStatusName() {
if (statusList.value.length > 0) {
const item = statusList.value.find(v => v.dictValue === form.status)
statusName.value = item ? item.dictLabel : ''
}
}
// 公告类型选择确认
function handleNoticeTypeConfirm(e) {
form.noticeType = e.value[0].dictValue
noticeTypeName.value = e.value[0].dictLabel
showNoticeTypePicker.value = false
}
// 状态选择确认
function handleStatusConfirm(e) {
form.status = e.value[0].dictValue
statusName.value = e.value[0].dictLabel
showStatusPicker.value = false
}
onReady(() => {
// 页面准备完成后的一些初始化操作
})
// 编辑器初始化完成
function onEditorReady() {
uni.createSelectorQuery().select('#editor').context((res) => {
editorCtx.value = res.context
// 如果是编辑模式,设置初始内容
if (form.noticeContent) {
editorCtx.value.setContents({
html: form.noticeContent
})
}
}).exec()
}
// 编辑器内容变化
function onEditorInput(e) {
// 获取HTML内容
editorCtx.value.getContents({
success: (res) => {
// 使用 Vue 的响应式更新方式
form.noticeContent = res.html
// 计算内容长度去除HTML标签
const text = res.text || ''
contentLength.value = text.length
}
})
}
// 编辑器获得焦点
function editorFocus() {
// 可以添加焦点样式
}
// 编辑器失去焦点
function editorBlur() {
// 保存内容
if (editorCtx.value) {
editorCtx.value.getContents({
success: (res) => {
form.noticeContent = res.html
}
})
}
}
// 格式化文本
function format(name, value) {
if (!editorCtx.value) return
editorCtx.value.format(name, value)
}
// 撤销
function undo() {
if (!editorCtx.value) return
editorCtx.value.undo()
}
// 重做
function redo() {
if (!editorCtx.value) return
editorCtx.value.redo()
}
// 插入图片
function insertImage() {
uni.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
const tempFilePath = res.tempFilePaths[0]
// 显示加载提示
uni.showLoading({
title: '上传中...'
})
// 这里需要根据实际的上传接口进行调整
// 示例代码,实际使用时需要替换为真实的上传逻辑
uni.uploadFile({
url: '/common/upload', // 替换为实际的上传地址
filePath: tempFilePath,
name: 'file',
success: (uploadRes) => {
uni.hideLoading()
try {
const data = JSON.parse(uploadRes.data)
if (data.code === 200) {
// 获取图片URL - 需要根据实际返回结构调整
const imageUrl = data.url || data.data || data.fileName
// 插入图片到编辑器
if (editorCtx.value) {
editorCtx.value.insertImage({
src: imageUrl,
width: '100%',
height: 'auto',
success: () => {
uni.showToast({
title: '插入成功',
icon: 'success'
})
},
fail: (err) => {
console.error('插入图片失败', err)
uni.showToast({
title: '插入失败',
icon: 'none'
})
}
})
}
} else {
uni.showToast({
title: data.msg || '上传失败',
icon: 'none'
})
}
} catch (e) {
console.error('解析上传结果失败', e)
uni.showToast({
title: '上传失败',
icon: 'none'
})
}
},
fail: (err) => {
uni.hideLoading()
console.error('上传失败', err)
uni.showToast({
title: '上传失败',
icon: 'none'
})
}
})
},
fail: (err) => {
console.error('选择图片失败', err)
}
})
}
// 提交表单
async function submitForm() {
try {
// 提交前确保获取最新的编辑器内容
if (editorCtx.value) {
const content = await new Promise((resolve, reject) => {
editorCtx.value.getContents({
success: (res) => {
form.noticeContent = res.html
resolve(res.html)
},
fail: reject
})
})
// 确保内容已更新到表单
await nextTick()
}
doSubmit()
} catch (error) {
modal.msgError('获取内容失败,请重试')
}
}
function doSubmit() {
errorFields.value = [] // 清空错误字段
if (!form.noticeTitle) {
errorFields.value.push('noticeTitle')
modal.msgError('请输入公告标题')
return
}
if (!form.noticeType) {
errorFields.value.push('noticeType')
modal.msgError('请选择公告类型')
return
}
if (!form.noticeContent || form.noticeContent.trim() === '') {
errorFields.value.push('noticeContent')
modal.msgError('请输入公告内容')
return
}
const submitForm = { ...form }
if (noticeId.value) {
updateNotice(submitForm).then(response => {
modal.msgSuccess('修改成功')
setTimeout(() => {
uni.navigateBack()
}, 1000)
}).catch(error => {
modal.msgError('修改失败: ' + (error.message || '未知错误'))
})
} else {
addNotice(submitForm).then(response => {
modal.msgSuccess('新增成功')
setTimeout(() => {
uni.navigateBack()
}, 1000)
}).catch(error => {
modal.msgError('新增失败: ' + (error.message || '未知错误'))
})
}
}
</script>
<style lang="scss" scoped>
.container {
background-color: #f5f5f5;
}
.section {
margin: 24rpx;
padding: 0;
background-color: #fff;
border-radius: 16rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
overflow: hidden;
.section-title {
padding: 16rpx 24rpx;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #ffffff;
line-height: 1.2;
font-size: 28rpx;
font-weight: 600;
display: flex;
align-items: center;
&::before {
content: '';
width: 6rpx;
height: 28rpx;
background: #ffffff;
border-radius: 3rpx;
margin-right: 12rpx;
}
}
.form-view {
padding: 24rpx;
.form-btn {
padding-top: 16rpx;
}
}
}
.editor-container {
width: 100%;
border: 2rpx solid #e8edf3;
border-radius: 12rpx;
overflow: hidden;
background: #ffffff;
box-sizing: border-box;
.editor-toolbar {
display: flex;
align-items: center;
padding: 12rpx;
background: #f8f9fa;
border-bottom: 2rpx solid #e8edf3;
flex-wrap: wrap;
gap: 8rpx;
.toolbar-item {
display: flex;
align-items: center;
justify-content: center;
width: 56rpx;
height: 56rpx;
background: #ffffff;
border-radius: 8rpx;
border: 2rpx solid #e8edf3;
transition: all 0.3s ease;
&:active {
transform: scale(0.95);
background: #667eea;
border-color: #667eea;
.iconfont {
color: #ffffff;
}
uni-icons {
color: #ffffff;
}
}
.iconfont {
font-size: 28rpx;
font-weight: 600;
color: #333333;
&.italic {
font-style: italic;
}
&.underline {
text-decoration: underline;
}
}
}
.toolbar-divider {
width: 2rpx;
height: 40rpx;
background: #e8edf3;
margin: 0 4rpx;
}
}
.editor-content {
width: 100%;
min-height: 500rpx;
padding: 20rpx;
font-size: 28rpx;
line-height: 1.6;
background: #ffffff;
box-sizing: border-box;
}
.editor-counter {
padding: 12rpx 20rpx;
background: #f8f9fa;
border-top: 2rpx solid #e8edf3;
text-align: right;
text {
font-size: 24rpx;
color: #999999;
}
}
}
</style>
<style lang="scss">
.form-btn .u-button {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
border: none !important;
border-radius: 24rpx !important;
height: 80rpx !important;
box-shadow: 0 4rpx 16rpx rgba(102, 126, 234, 0.4) !important;
}
.form-btn .u-button__text {
font-size: 30rpx !important;
font-weight: 500 !important;
letter-spacing: 2rpx !important;
}
</style>