feat(goods): 新增文章编辑功能并优化列表页面

- 新增文章编辑页面,支持添加和编辑文章
- 优化文章列表页面,增加详情对话框和删除功能
- 更新相关 API 接口,支持文章添加和编辑- 调整用户列表页面布局
- 移除底部边框样式
This commit is contained in:
阿怪 2025-05-11 22:05:00 +08:00
parent 480f1e02ed
commit e6ceacfae1
5 changed files with 123 additions and 48 deletions

View File

@ -15,21 +15,21 @@ export const list = (params) => {
export const add = (data) => { export const add = (data) => {
return service({ return service({
url: '/article/list', url: '/article',
method: 'post', method: 'post',
data data
}) })
} }
export const edit = (data) => { export const edit = (data) => {
return service({ return service({
url: '/article/list', url: '/article',
method: 'put', method: 'put',
data data
}) })
} }
export const del = (data) => { export const del = (data) => {
return service({ return service({
url: '/article/list', url: '/article',
method: 'delete', method: 'delete',
data data
}) })

View File

@ -64,5 +64,5 @@
padding: 1rem 0; padding: 1rem 0;
position: sticky; position: sticky;
bottom: 0; bottom: 0;
border-top: 1px solid #ededed; //border-top: 1px solid #ededed;
} }

View File

@ -24,16 +24,7 @@
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :span="8"> <el-col :span="8">
<el-form-item label="图片" prop="coverImg"> <el-form-item label="图片" prop="coverImg">
<el-upload <SelectImage v-model="formData.coverImg" />
class="avatar-uploader"
action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15"
:show-file-list="false"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload"
>
<img v-if="formData.coverImg" :src="formData.coverImg" class="avatar" alt=""/>
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
</el-upload>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="8">
@ -46,7 +37,7 @@
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :span="20"> <el-col :span="20">
<el-form-item label="详情" prop="content"> <el-form-item label="详情" prop="content">
<RichEdit v-model="formData.content"/> <RichEdit style="width: 100%" v-model="formData.content"/>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
@ -63,7 +54,7 @@
</el-form> </el-form>
<div class="gva-table-box footer-box"> <div class="gva-table-box footer-box">
<el-button @click="$router.back()">返回</el-button> <el-button @click="$router.back()">返回</el-button>
<el-button type="primary" @click="submit">提交</el-button> <el-button type="primary" @click="submit(ruleFormRef)">提交</el-button>
</div> </div>
@ -76,28 +67,42 @@
/> />
</template> </template>
<script setup> <script setup>
import SelectImage from '@/components/selectImage/selectImage.vue'
import {onMounted, ref } from 'vue' import {onMounted, ref } from 'vue'
import userChoose from '@/components/userChoose/index.vue' import userChoose from '@/components/userChoose/index.vue'
import { useRouter, useRoute} from 'vue-router' import { useRouter, useRoute} from 'vue-router'
import {detail} from '@/api/goods/index.js' import { add, detail, edit } from '@/api/goods/index.js'
import ColumnItem from '@/components/columnItem/ColumnItem.vue' import ColumnItem from '@/components/columnItem/ColumnItem.vue'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { Plus } from '@element-plus/icons-vue'
import RichEdit from '@/components/richtext/rich-edit.vue' import RichEdit from '@/components/richtext/rich-edit.vue'
const router = useRouter() const router = useRouter()
const route = useRoute() const route = useRoute()
const userDialogRef = ref() const userDialogRef = ref()
const isEdit = ref(false)
const ruleFormRef = ref(null), formData = ref({}), rules = ref({ const ruleFormRef = ref(null), formData = ref({}), rules = ref({
title: [ title: [
{ required: true, message: '请输入文章标题', trigger: 'blur' } { required: true, message: '请输入文章标题', trigger: 'blur' }
], ],
price: [
{ required: true, message: '请输入价格', trigger: 'blur' }
],
coverImg: [
{ required: true, message: '请上传封面', trigger: 'blur' }
],
teacher: [
{ required: true, message: '请选择讲师', trigger: 'blur' }
],
content: [
{ required: true, message: '请输入文章内容', trigger: 'blur' }
]
}) })
onMounted(() => { onMounted(() => {
console.log(route.query.id) console.log(route.query.id)
if(route.query.id) { if(route.query.id) {
getDetail() isEdit.value = true
getDetail()
} }
}) })
const handleAvatarSuccess = ( response, uploadFile ) => { const handleAvatarSuccess = ( response, uploadFile ) => {
@ -120,13 +125,32 @@
} }
} }
function getStaffInfo(data) { function getStaffInfo(data) {
console.log(data)
formData.value.teacherId = data.ID formData.value.teacherId = data.ID
formData.value.teacher = data.nickName formData.value.teacher = data.nickName
} }
function submit() { function submit(formRef) {
console.log('提交') if(!formRef) return
//
formRef.validate(async (valid) => {
if(!valid) return
let fn = isEdit.value ? edit : add
// price
formData.value.price = Number(formData.value.price)
const res = await fn(formData.value)
console.log(res)
if(res.code === 0) {
ElMessage({
type: 'success',
message: isEdit.value ? '编辑成功' : '添加成功!'
})
router.back()
} else {
ElMessage({
type: 'error',
message: res.msg
})
}
})
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -10,7 +10,7 @@
</div> </div>
<div class="gva-table-box"> <div class="gva-table-box">
<div class="gva-btn-list"> <div class="gva-btn-list">
<el-button type="primary" icon="plus" @click="openDialog()">新增</el-button> <el-button type="primary" icon="plus" @click="handleEdit()">新增</el-button>
</div> </div>
<Content <Content
@changePage="changePage" @changePage="changePage"
@ -53,12 +53,38 @@
</template> </template>
</Content> </Content>
</div> </div>
<!-- template Content 组件后添加 Dialog -->
<el-dialog
v-model="detailVisible"
title="文章详情"
width="60%"
destroy-on-close
>
<el-descriptions :column="2" border>
<el-descriptions-item label="封面图">
<el-image
style="width: 100px; height: 100px"
:src="detailData.coverImg"
:preview-src-list="[detailData.coverImg]"
fit="cover"
/>
</el-descriptions-item>
<el-descriptions-item label="商品名称">{{ detailData.title || EMPTY_STR }}</el-descriptions-item>
<el-descriptions-item label="创建时间">{{ formatDate(detailData.CreatedAt) }}</el-descriptions-item>
<el-descriptions-item label="更新时间">{{ formatDate(detailData.UpdatedAt) }}</el-descriptions-item>
<el-descriptions-item label="商品描述" :span="2">
<div v-html="detailData.content"></div>
</el-descriptions-item>
</el-descriptions>
</el-dialog>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ElMessage, ElMessageBox } from 'element-plus'
import searchForm from '@/components/searchForm/index.vue' import searchForm from '@/components/searchForm/index.vue'
import Content from '@/components/content/index.vue' import Content from '@/components/content/index.vue'
import {list} from '@/api/goods/index.js' import { del, list } from '@/api/goods/index.js'
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import {formatDate} from '@/utils/format' import {formatDate} from '@/utils/format'
import { ARTICLE_SEARCH_CONFIG, ARTICLE_TABLE_CONFIG } from '@/config' import { ARTICLE_SEARCH_CONFIG, ARTICLE_TABLE_CONFIG } from '@/config'
@ -66,9 +92,12 @@
import { useRouter, useRoute} from 'vue-router' import { useRouter, useRoute} from 'vue-router'
const router = useRouter() const router = useRouter()
const route = useRoute() const route = useRoute()
const EMPTY_STR = '- -'
// script setup
const detailVisible = ref(false)
const detailData = ref({})
const tableLoading = ref(true), tableData = ref([])
const tableLoading = ref(true), tableData = ref([{status:1}])
const queryParams = ref({ const queryParams = ref({
page: 1, page: 1,
pageSize: 10, pageSize: 10,
@ -76,10 +105,6 @@
startTime: '', startTime: '',
endTime: '' endTime: ''
}), total = ref(0) }), total = ref(0)
// const EMPTY_STR = '- -'
const tag = (status) => {
return userStatus.find((item) => item.value === status+'') || { type: '', label: EMPTY_STR }
}
const searchData = (data) => { const searchData = (data) => {
if (data.times) { if (data.times) {
data.startTime = data.times[0] data.startTime = data.times[0]
@ -100,6 +125,11 @@
onMounted(() => { onMounted(() => {
getList() getList()
}) })
function handleDetail(row) {
detailData.value = { ...row }
detailVisible.value = true
}
async function getList() { async function getList() {
const res = await list(queryParams.value) const res = await list(queryParams.value)
if(res.code === 0) { if(res.code === 0) {
@ -107,21 +137,49 @@
total.value = res.data.total total.value = res.data.total
} }
} }
function handleDetail(row) {
}
function handleEdit(row) { function handleEdit(row) {
console.log(row)
router.push({ router.push({
name: 'edit', name: 'edit',
query: { query: {
id: row.ID id: row?row.ID:''
} }
}) })
} }
function handleDelete(row) { async function handleDelete(row) {
//
// this.$confirm(', ?', '', {
// confirmButtonText: '',
// cancelButtonText: '',
// type: 'warning'
// }).then(() => {
// //
// del(row.ID)
// }).catch(() => {
// this.$message({
// type: 'info',
// message: ''
// });
// });
try {
await ElMessageBox.confirm('此操作将永久删除该商品,是否继续?', '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
const res = await del({
ids: [row.ID]
})
if (res.code === 0) {
ElMessage.success('删除成功')
getList() //
}
} catch (error) {
//
if (error !== 'cancel') {
ElMessage.error(error.message || '删除失败')
}
}
} }
function changePage(data) { function changePage(data) {
queryParams.value.pageNum = data queryParams.value.pageNum = data

View File

@ -7,9 +7,11 @@
@resetData="resetData" @resetData="resetData"
class="search-box searchForm" class="search-box searchForm"
/> />
<el-button type="primary" @click="handleAdd">新增用户</el-button>
</div> </div>
<div class="gva-table-box"> <div class="gva-table-box">
<div class="gva-btn-list">
<el-button type="primary" @click="handleAdd">新增用户</el-button>
</div>
<Content <Content
@changePage="changePage" @changePage="changePage"
:total="total" :total="total"
@ -359,13 +361,4 @@
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.searchForm {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.el-descriptions {
margin: 20px 0;
}
</style> </style>