系列文章目录
第一章 系统功能规划
第二章 登录界面实现
第三章 系统框架搭建
第四章 主页框架实现
第五章 首页界面实现
第六章 新闻资讯界面实现
第七章 访问统计界面实现
第八章 内容配置界面实现
第九章 系统设置界面实现
第十章 代码重构及项目发布
文章目录
本篇实现新闻资讯相关界面,包括列表、编辑以及详情界面等。列表界面使用动态配置的方式实现,通过配置实体要展示的列信息(标识、显示名、排序、类型、宽度、解析方式等),可将列表部分提取成一个单独的组件,在其他地方直接调用。
一、效果预览
二、开发视频
vue实战入门进阶篇:vue+elementui实现网站管理后台界面
三、界面实现
3.1 框架搭建
搭建新闻资讯界面基础框架,包括顶部的操作按钮、表格的搜索功能、列表加载、列表操作按钮、页码实现等,实现代码如下:
1. 模板代码:
<template>
<ListPage ref="listtable" :userinfo="userinfo" :needadd="needadd" :needdelete="needdelete" :needdownload="needdownload" :neededit="neededit" :entitytype="entitytype" :dataurl="dataurl" :check="check" :optionwidth="optionwidth" @dictloaded="dictloaded" @toinfo="toinfo" @toform="toform"></ListPage>
<!-- 添加数据对话框,可将内容提取到一个单独的组件中进行调用 -->
<NewsForm ref="form" :userinfo="userinfo" @addsuccess="addsuccess"></NewsForm>
<NewsInfo ref="info" @edit="edit" :dicts="dicts"></NewsInfo>
</template>
2. JS代码:
<script>
import utils from "../../public/utils.js";
import api from "../../public/api.js";
import config from "../../public/config.js";
import ListPage from "../../components/ListPage.vue";
import NewsForm from "./NewsForm.vue";
import NewsInfo from "./NewsInfo.vue";
import $ from 'jquery';
export default {
props: ['userinfo'],
components:{
ListPage,
NewsForm,
NewsInfo
},
data() {
return {
needadd:true,
needdelete:true,
needdownload:true,
neededit:true,
entitytype:'news',
optionwidth:180,
check:true,
dataurl:api.server_urls.new_url,
dicts:[]
}
},
methods: {
dictloaded:function(dicts){
this.dicts = dicts;
},
toinfo:function(row){
this.$refs.info.show(row);
},
toform:function(row){
this.$refs.form.show(row);
},
addsuccess:function(data){
this.$refs.listtable.addsuccess(data);
}
}
}
</script>
3. CSS代码:
无
3.2 列表组件
列表组件,根据后台返回的实体配置信息,自动生成对应的表格及搜索项,并封装成单独组件以便其它地方也可以调用,实现代码如下:
1. 模板代码:
<template>
<div class="operation">
<el-button-group>
<el-button type="primary" @click="add" v-if="needadd">
<el-icon>
<plus />
</el-icon>
<span>添加数据</span>
</el-button>
<!-- <el-button type="primary">
<el-icon>
<upload />
</el-icon>
<span>导入数据</span>
</el-button> -->
<el-button type="danger" @click="delete_" v-if="needdelete">
<el-icon>
<delete />
</el-icon>
<span>批量刪除</span>
</el-button>
</el-button-group>
<el-button-group style="margin-left: 10px;" v-if="needdownload">
<el-dropdown>
<el-button type="primary">
<el-icon>
<download />
</el-icon>
<span>导出数据</span>
<el-icon class="el-icon--right">
<arrow-down />
</el-icon>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="exportCheck">导出全部</el-dropdown-item>
<el-dropdown-item @click="exportAll">导出选中</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</el-button-group>
</div>
<!-- 列表区 -->
<div class="container">
<el-row>
<el-col :span="13" style="line-height: 60px;">
<!-- 当前搜索项目展示 -->
<span>搜索项目:</span>
<template v-for="(item,index) in table.search.defaultsearch">
<el-tag class="mx-1" closable type="danger">{{ item.key.label }}:{{ item.value }}</el-tag>
</template>
<span v-if="!table.search.defaultsearch || table.search.defaultsearch.length<=0">无</span>
</el-col>
<el-col :span="11">
<!-- 搜索功能区 -->
<el-form :model="table.search.searchform" label-width="100px">
<el-form-item label="快捷搜索:" :gutter="10">
<el-col :span="3">
<el-select v-model="table.search.searchform.type" placeholder="项目"
@change="searchTypeChange">
<template v-for="(item,index) in table.entityfileds" :key="item.name">
<el-option :label="item.label" :value="item.name" v-if="item.canDefaultSearch" />
</template>
</el-select>
</el-col>
<el-col :span="7">
<div style="padding-left:10px">
<!-- 此处的内容动态生成 -->
<template v-for="(item,index) in table.entityfileds" :key="item.name">
<el-input v-model="table.search.searchform.value"
v-if="item.name == table.search.searchform.type && item.canDefaultSearch && (item.type == 'text' || item.type == 'textarea')"
:placeholder="'请输入'+item.label" />
<el-select v-model="table.search.searchform.value"
v-else-if="item.name == table.search.searchform.type && item.canDefaultSearch && (item.type == 'dict')"
:placeholder="'请选择'+item.label" :multiple="item.canSearchMultiple">
<template v-if="table.dicts[item.dictkey]"
v-for="(dictitem,dictindex) in table.dicts[item.dictkey]">
<el-option :label="dictitem.label" :value="dictitem.label" />
</template>
</el-select>
<el-select v-model="table.search.searchform.value"
v-else-if="item.name == table.search.searchform.type && item.canDefaultSearch && (item.type == 'object')"
:placeholder="'请选择'+item.label">
</el-select>
<el-date-picker style="width:100%" v-model="table.search.searchform.value"
v-else-if="item.name == table.search.searchform.type && item.canDefaultSearch && (item.type == 'date')"
:placeholder="'请选择'+item.label" type="datetime" clearable editable
:format="item.formate" />
</template>
</div>
</el-col>
<el-col :span="1"></el-col>
<el-col :span="13">
<el-button-group>
<el-button type="primary" @click="search">
<el-icon>
<search />
</el-icon>
<span>搜索数据</span>
</el-button>
<el-button type="primary" @click="refresh">
<el-icon>
<refresh />
</el-icon>
<span>刷新列表</span>
</el-button>
<el-button type="danger" @click="searchmore">
<el-icon>
<operation />
</el-icon>
<span>高级搜索</span>
</el-button>
</el-button-group>
</el-col>
</el-form-item>
</el-form>
</el-col>
</el-row>
<div>
<!-- 列表区 -->
<el-table ref="tableele" :data="table.page.result.datas" style="width:100%" border stripe
:height="(height-60-32-36-10-80)" highlight-current-row="true" @cell-dblclick="dbHandleInfo" v-loading="loading">
<el-table-column type="selection" width="55" fixed align="center" v-if="check" />
<template v-for="(item,index) in table.entityfileds" :key="item.name">
<el-table-column resizable show-overflow-tooltip="true" :align="item.align" :label="item.label"
:width="item.width" :sortable="item.sortable" :property="item.name"
v-if="item.type == 'date' && item.defaultShow">
<template #default="scope">
{{scope.row[item.name]&&item.formate ?new Date(scope.row[item.name]).format(item.formate):""}}
</template>
</el-table-column>
<el-table-column resizable show-overflow-tooltip="true" :align="item.align" :label="item.label"
:width="item.width" :sortable="item.sortable" :property="item.name"
v-else-if="item.type == 'image' && item.show">
<template #default="scope">
<el-image z-index="9999" :src="scope.row[item.name]" lazy
:preview-src-list="table.page.result.images" :initial-index="index" fit="cover" />
</template>
</el-table-column>
<el-table-column resizable show-overflow-tooltip="true" :align="item.align" :label="item.label"
:width="item.width" :sortable="item.sortable" :property="item.name"
v-else-if="item.type == 'object' && item.show">
<template #default="scope">
{{scope.row[item.name]?scope.row[item.name][item.showname]:""}}
</template>
</el-table-column>
<el-table-column resizable show-overflow-tooltip="true" :align="item.align" :label="item.label"
:width="item.width" :sortable="item.sortable" :property="item.name"
v-else-if="item.type == 'dict' && item.defaultShow" :formatter="dictformatter">
<template #default="scope">
<template v-if="table.dicts[item.dictkey]"
v-for="(dictitem,dictindex) in table.dicts[item.dictkey]">
<el-tag class="mx-1" effect="dark" v-if="scope.row[item.name] == dictitem.value"
:type="colors[dictindex]">{{ dictitem.label }}</el-tag>
</template>
</template>
</el-table-column>
<el-table-column resizable show-overflow-tooltip="true" :align="item.align" :label="item.label"
:width="item.width" :sortable="item.sortable" :property="item.name"
v-else-if="item.defaultShow" />
</template>
<el-table-column label="操作" :width="optionwidth">
<template #default="scope">
<el-button-group>
<el-button size="small" @click="handleEdit(scope.$index, scope.row)" v-if="neededit">编辑</el-button>
<el-button size="small" @click="handleInfo(scope.$index, scope.row)">详情</el-button>
<el-button size="small" type="danger" @click="handleDelete(scope.$index, scope.row)" v-if="needdelete">删除
</el-button>
</el-button-group>
</template>
</el-table-column>
</el-table>
<!-- 分页区 -->
<div style="padding-top: 10px;">
<el-pagination v-model:currentPage="table.page.pama.pageNo" background v-model:page-size="table.page.pama.pageSize"
:page-sizes="table.page.pama.pagelimits" :default-page-size="table.page.pama.pageSize" page-count="table.page.result.totalPage" :small="small"
layout="total, sizes, prev, pager, next, jumper" :total="table.page.result.total"
@size-change="handleSizeChange" @current-change="handleCurrentChange" @prev-click="handlePreChange" @next-click="handleNextChange"/>
</div>
</div>
</div>
<!-- 添加数据对话框,可将内容提取到一个单独的组件中进行调用 -->
<!-- <component :is="formcomponent" v-if="neededit || needadd" ref="form" :userinfo="userinfo" @addsuccess="addsuccess"></component>
<component :is="infocomponent" ref="info" @edit="edit" :dicts="table.dicts"></component> -->
</template>
2. JS代码:
<script>
import {
defineAsyncComponent
} from 'vue'
import utils from "../public/utils.js";
import api from "../public/api.js";
import config from "../public/config.js";
import $ from 'jquery';
export default {
props: ['userinfo',"needadd","needdelete","needdownload","neededit","entitytype","dataurl","optionwidth","check"],
data() {
return {
colors: config.colors,
height: $(window).height(),
table: {
dicts: {}, //列表要用到的数据字典,从后台加载
entityfileds: [],
search: {
defaultsearch: [],
searchform: {
type: "",
value: ""
}
},
page: {
pama: {
pageSize: config.table.pageSize,
pageNo: config.table.pageNo,
pageLimits: config.table.pageLimits
},
result: {
totalPage: 0,
total:0,
images: [], //用于图片预览
datas: []
}
}
}
}
},
mounted: function() {
var that = this;
//加载列表属性配置
api.loadEntityFiledsDatas(function(res) {
if (!res || res.status != 200 || !res.data) {
utils.showerror("信息加载失败!");
return;
}
res.data.news.sort(function(a, b) {
return a.sort - b.sort;
});
var fristtype = null;
res.data[that.entitytype].forEach(function(item, index) {
that.table.entityfileds.push(item);
if (fristtype == null && item.canDefaultSearch) {
fristtype = item;
}
})
if (fristtype) {
that.table.search.searchform.type = fristtype.name;
}
that.$forceUpdate();
//字段菜蔬加载完毕后再加载列表数据
that.loadData();
});
//加载数据字典数据,
api.loadDictDatas(function(res) {
if (!res || res.status != 200 || !res.data) {
utils.showerror("信息加载失败!");
return;
}
for (var key in res.data) {
that.table.dicts[key] = res.data[key];
}
that.$forceUpdate();
that.$emit('dictloaded', that.table.dicts);
});
},
methods: {
loadData: function() {
var that = this;
var entity = {};
entity[this.table.search.searchform.type] = this.table.search.searchform.value;
api.loadPageDatas(this.dataurl, {
pageSize: this.table.page.pama.pageSize,
pageNo: this.table.page.pama.pageNo,
entity: entity
}, function(res) {
if (!res || res.status != 200 || !res.data) {
utils.showerror("信息加载失败!");
return;
}
that.table.page.result.totalPage = res.data.totalPage;
that.table.page.result.total = res.data.total;
res.data.datas.forEach(function(item, index) {
if(that.table.search.searchform.value && item[that.table.search.searchform.type] != that.table.search.searchform.value){//模拟,不符合搜索条件,但是页码会有问题
return;
}
that.table.page.result.datas.push(item);
that.table.page.result.images.push(item.icon);
})
that.$forceUpdate();
});
},
refresh:function(){
//清除旧的数据
this.table.page.result.datas.splice(0, this.table.page.result.datas.length);
this.table.page.result.images.splice(0, this.table.page.result.datas.length);
this.loadData();
},
search:function(){
this.table.page.pama.pageNo = config.table.pageNo;
this.refresh();
//此处显示当前搜索条件
this.table.search.defaultsearch.splice(0, this.table.search.defaultsearch.length);
var key = null;
for (var i = 0; i < this.table.entityfileds.length; i++) {
if(this.table.entityfileds[i].name == this.table.search.searchform.type){
key = this.table.entityfileds[i];
break;
}
}
if(key && this.table.search.searchform.value){
this.table.search.defaultsearch.push({
key: key,
value: this.table.search.searchform.value
});
}
},
searchmore:function(){
// 弹出高级搜索框,可进行复合搜索
utils.showerror("待开发");
},
searchTypeChange: function() {
this.table.search.searchform.value = null;
this.$forceUpdate();
},
handleSizeChange: function() {
this.refresh();
},
handleCurrentChange: function() {
this.refresh();
},
add: function() { //添加数据
// this.$refs.form.show(null);
this.$emit('toform', null);
},
addsuccess: function(data) { //添加数据
var olditem = null;
var olditemindex = null;
this.table.page.result.datas.forEach(function(item,index){
if(data.id == item.id){
olditem = item;
olditemindex = index;
}
})
if(olditem!=null){//更新
this.table.page.result.datas[olditemindex] = data;
this.table.page.result.images[olditemindex] = data.icon;
}else{
this.table.page.result.datas.push(data);
this.table.page.result.images.push(data.icon);
this.table.page.result.total = this.table.page.result.total+1;
this.table.page.result.totalPage = this.table.page.result.total/this.table.page.pama.pageSize;
if(this.table.page.result.total%this.table.page.pama.pageSize!=0){
this.table.page.result.totalPage++;
}
}
},
delete_: function() {
var datas = this.$refs.tableele.getSelectionRows();
if (!datas || datas.length <= 0) {
utils.showerror("请至少选中一条数据");
return;
}
var that = this;
utils.confirm('确定', '确定要删除这几条数据吗?', function(res) {
if (res != 'success') {
return;
}
datas.forEach(function(item, index) {
for (var i = 0; i < that.table.page.result.datas.length; i++) {
if (that.table.page.result.datas[i].id == item.id) {
that.table.page.result.datas.splice(i, 1);
that.table.page.result.images.splice(i, 1);
i--;
that.table.page.result.total = that.table.page.result.total-1;
that.table.page.result.totalPage = that.table.page.result.total/that.table.page.pama.pageSize;
if(that.table.page.result.total%that.table.page.pama.pageSize!=0){
that.table.page.result.totalPage++;
}
}
}
})
})
},
exportCheck: function() {
var datas = this.$refs.tableele.getSelectionRows();
if (!datas || datas.length <= 0) {
utils.showerror("请至少选中一条数据");
return;
}
//需后台模拟
utils.showsuccess("导出成功");
},
exportAll: function() {
//需后台模拟
utils.showsuccess("导出成功");
},
dbHandleInfo:function(row, column, cell, event){
this.handleInfo(null,row);
},
handleEdit:function(index,row){
// this.$refs.form.show(row);
this.$emit('toform', row);
},
handleInfo:function(index,row){
// this.$refs.info.show(row);
this.$emit('toinfo', row);
},
edit:function(row){
// this.$refs.form.show(row);
this.$emit('toform', row);
},
handleDelete:function(index,row){
var that = this;
utils.confirm('确定', '确定要删除吗?', function(res) {
if (res != 'success') {
return;
}
for (var i = 0; i < that.table.page.result.datas.length; i++) {
if (that.table.page.result.datas[i].id == row.id) {
that.table.page.result.datas.splice(i, 1);
that.table.page.result.images.splice(i, 1);
i--;
that.table.page.result.total = that.table.page.result.total-1;
that.table.page.result.totalPage = that.table.page.result.total/that.table.page.pama.pageSize;
if(that.table.page.result.total%that.table.page.pama.pageSize!=0){
that.table.page.result.totalPage++;
}
}
}
})
}
}
}
</script>
3. CSS代码:
无
3.3 编辑界面
新闻资讯编辑界面,实现新闻资讯数据编辑功能,实现代码如下:
1. 模板代码:
<template>
<el-dialog v-model="addDialogForm.visible" title="添加数据" center draggable destroy-on-close width="80%">
<el-scrollbar :max-height="height*0.8-200">
<el-form :model="addDialogForm.form" ref="addDialogForm" :rules="addDialogForm.rules">
<el-row>
<el-col :span="12">
<el-form-item label="标题" :label-width="120">
<el-input v-model="addDialogForm.form.title" required placeholder="请输入标题" autocomplete="off"
style="width:58%" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="副标题" :label-width="120">
<el-input v-model="addDialogForm.form.subtitle" placeholder="请输入副标题" autocomplete="off"
style="width:58%" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="图标" :label-width="120">
<el-upload class="upload-demo" drag action="文件上上传地址" limit="1">
<el-icon class="el-icon--upload">
<upload-filled />
</el-icon>
<div class="el-upload__text">
拖动到此处或者 <em>点击此处上传</em>
</div>
<template #tip>
<div class="el-upload__tip">
jpg/png 文件且大小不能超过 500kb
</div>
</template>
</el-upload>
</el-form-item>
</el-col>
<el-col :span="12"></el-col>
</el-row>
<!-- /内容使用富文本的方式 -->
<el-form-item label="内容" :label-width="120">
<div id="content" style="height:350px;width: 95%;"></div>
</el-form-item>
<el-form-item label="备注" :label-width="120">
<el-input v-model="addDialogForm.form.description" :rows="5" type="textarea" placeholder="请输入备注"
autocomplete="off" style="width:95%" />
</el-form-item>
</el-form>
</el-scrollbar>
<template #footer>
<span class="dialog-footer">
<el-button @click="addDialogForm.visible = false">取消</el-button>
<el-button type="primary" @click="submitAdd">确定</el-button>
</span>
</template>
</el-dialog>
</template>
2. JS代码:
<script>
import utils from "../../public/utils.js";
import api from "../../public/api.js";
import config from "../../public/config.js";
import $ from 'jquery';
import E from "wangeditor"
export default {
props: ['userinfo'],
data() {
return {
height: $(window).height(),
addDialogForm: { //表单也可以根据实体配置自动生成,此处暂不处理,后续进行扩展
visible: false,
editor: null,
form: {
id:null,
title: '',
subtitle: '',
icon: '',
content: '',
description: ''
},
rules: {
title: [{
required: true,
message: '请输入标题',
trigger: 'blur'
},
{
min: 1,
max: 1024,
message: '长度必须大于0小于1024',
trigger: 'blur'
}
],
subtitle: [{
min: 0,
max: 1024,
message: '长度必须大于0小于1024',
trigger: 'blur'
}]
}
},
}
},
methods: {
submitAdd: function() {
//获取表单数据,并进行验证等
var isok = true;
this.$refs.addDialogForm.validate(function(valid, fields) {
console.log(valid);
console.log(fields);
if (!valid) {
isok = false;
return;
}
})
if (!isok) {
utils.showerror("请检查表单信息");
return;
}
//图标,此处需配合后台,暂不测试
this.addDialogForm.form.icon = '/src/assets/1.jpeg';
//内容
this.addDialogForm.form.content = this.addDialogForm.editor.txt.html();
if (!this.addDialogForm.form.title) {
utils.showerror("标题不能为空");
return;
}
if (!this.addDialogForm.form.content) {
utils.showerror("內容不能为空");
return;
}
//这些参数后台自动生成
if(!this.addDialogForm.form.id){
this.addDialogForm.form.status = "1";
this.addDialogForm.form.user = this.userinfo;
this.addDialogForm.form.createDate = new Date().getTime();
this.addDialogForm.form.id = new Date().getTime();
this.addDialogForm.form.viewcount = 0;
}
utils.showsuccess("添加成功");
this.addDialogForm.visible = false;
this.$emit('addsuccess',this.addDialogForm.form);
},
show:function(data){
//TODO 此处最好从后台再加载一次数据并填充到表单中
if(data){//对象赋值
for (let key in data) {
this.addDialogForm.form[key] = data[key];
}
}
this.addDialogForm.visible = true;
var that = this;
setTimeout(function() {
that.addDialogForm.editor = new E(document.getElementById('content'));
that.addDialogForm.editor.config.placeholder = '请输入新闻正文';
// 配置服务器端地址
// editor.config.uploadImgServer = '/src'
that.addDialogForm.editor.create();
//设置内容
that.addDialogForm.editor.txt.html(that.addDialogForm.form.content);
}, 200);
}
}
}
</script>
3. CSS代码:
<style>
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
transition: var(--el-transition-duration-fast);
}
.avatar-uploader .el-upload:hover {
border-color: var(--el-color-primary);
}
.el-icon.avatar-uploader-icon {
font-size: 20px;
color: #8c939d;
width: 45px;
height: 45px;
text-align: center;
}
</style>
3.4 详情界面
实现新闻资讯详情界面相关功能,实现代码如下:
1. 模板代码:
<template>
<el-dialog v-model="visible" title="详情" center draggable destroy-on-close width="80%">
<el-scrollbar :max-height="height*0.8-200">
<el-form :model="data" ref="Form">
<el-row>
<el-col :span="12">
<el-form-item label="标题:" :label-width="120">
<span>{{data.title}}</span>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="副标题:" :label-width="120">
<span>{{data.subtitle}}</span>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="状态:" :label-width="120">
<dictinfoitem :dicts="dicts['news_status']" :value="data.status"></dictinfoitem>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="创建人:" :label-width="120">
<span>{{data.user.name}}</span>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="创建时间:" :label-width="120">
<span>{{new Date(data.createDate).format('yyyy-MM-dd hh:mm:ss')}}</span>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="浏览量:" :label-width="120">
<span>{{data.viewcount}}</span>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="图标:" :label-width="120">
<el-image z-index="9999" :src="data.icon" lazy fit="cover" style="width:150px;" />
</el-form-item>
</el-col>
</el-row>
<!-- /内容使用富文本的方式 -->
<el-form-item label="内容:" :label-width="120">
<div id="content" style="width: 95%;">{{data.content}}</div>
</el-form-item>
<el-form-item label="备注:" :label-width="120">
{{data.description}}
</el-form-item>
</el-form>
</el-scrollbar>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<el-button type="danger" @click="edit">编辑</el-button>
</span>
</template>
</el-dialog>
</template>
2. JS代码:
<script>
import utils from "../../public/utils.js";
import api from "../../public/api.js";
import config from "../../public/config.js";
import $ from 'jquery';
import dictinfoitem from "../../components/dictinfoitem.vue";
export default {
props: ['dicts'],
components:{
dictinfoitem
},
data() {
return {
colors: config.colors,
height: $(window).height(),
visible: false,
data: {}
}
},
methods: {
show: function(data) {
//TODO 此处最好从后台再加载一次数据并填充到界面中
if (data) { //对象赋值
for (let key in data) {
this.data[key] = data[key];
}
}
this.visible = true;
},
edit: function() {
this.visible = false;
this.$emit('edit', this.data);
}
}
}
</script>
3. CSS代码:
无
四、本篇总结
本篇实现了新闻资讯界面列表页、详情页、编辑页、删除、简单搜索、数据导出等功能,使用字段配置方式实现要展示的内容设置,前端解析数据并进行相应数据展示及搜索。
下一篇:访问统计界面实现。