编辑新增等功能

使用ant-design的组件,关于引入和使用就不说了,把UserList.vue完整的代码放出来供大家参考,一些必要的地方也已经写好了注释,大部分代码都是重复的。和之前的有些许改变。后台也可以按照自己喜欢的风格来做。

<template>
	<div>
		<a-card>
			<a-row :gutter="20">
				<a-col :span="6">
					<a-input-search 
					v-model="queryParam.username"
					allowClear placeholder="请输入用户名" 
					enter-button 
					@search="getUserList" />
				</a-col>
				<a-col :span="4">
					<a-button type="primary" @click="AddUserVisible = true">新增</a-button>
				</a-col>
			</a-row>
			<a-table
				:columns='columns' 
				rowKey='ID'
				:pagination="pagination"
				:dataSource="userlist"
				bordered
				@change="handleTableChange"
			>
			<span slot="role" slot-scope="role">{{role == 1 ? "管理员":"订阅者"}}</span>
			<template slot="action" slot-scope="data">
				<div class="actionSlot">
				<a-button type="info" icon="info" style="margin-right:15px" @click="ChangePassword(data.ID)">修改密码</a-button>
				<a-button type="primary" icon="edit" style="margin-right:15px" @click="editUser(data.ID)">编辑</a-button>
				<a-popconfirm title="确认删除?删除后无法恢复" ok-text="确认" cancel-text="取消" @confirm="delUser(data.ID)">
				  <a-button icon="delete" type="danger">删除</a-button>
				</a-popconfirm>
			</div>
			</template>
			</a-table>
		</a-card>
		
		<!-- 新增用户对话框 -->
		<a-modal
      title="新增用户"
      :visible="AddUserVisible"
      @ok="AddUserOk"
      @cancel="addUserCanal"
			closable
    >
      <a-form-model :model="userinfo" :rules="userRules" ref="addUserRef">
				<a-form-model-item label="用户名" prop="username">
					<a-input v-model="userinfo.username"></a-input>
				</a-form-model-item>
				<a-form-model-item label="密码" prop="password">
					<a-input-password v-model="userinfo.password">

					</a-input-password>
				</a-form-model-item>
				<a-form-model-item label="确认密码" prop="checkpass">
					<a-input-password v-model="userinfo.checkpass">

					</a-input-password>
				</a-form-model-item>
				
			</a-form-model>
    </a-modal>

		<!-- 编辑用户 -->
		<a-modal
      title="编辑用户"
      :visible="editUserVisible"
      @ok="editUserOk"
			width="60%"
      @cancel="editUserCanal"
			closable
    >
      <a-form-model :model="userinfo" :rules="userRules" ref="addUserRef">
				<a-form-model-item label="用户名" prop="username">
					<a-input v-model="userinfo.username"></a-input>
				</a-form-model-item>

				<a-form-model-item label="是否为管理员">
          <a-switch
            :checked="IsAdmin"
            checked-children="是"
            un-checked-children="否"
            @change="adminChange"
          />
        </a-form-model-item>
				
			</a-form-model>
    </a-modal>

		<!-- 编辑密码 -->
		<a-modal
      closable
      title="修改密码"
      :visible="changePasswordVisible"
      width="60%"
      @ok="changePasswordOk"
      @cancel="changePasswordCancel"
      destroyOnClose
    >
      <a-form-model :model="userinfo" :rules="userRules" ref="addUserRef">
        <a-form-model-item has-feedback label="密码" prop="password">
          <a-input-password v-model="userinfo.password"></a-input-password>
        </a-form-model-item>
        <a-form-model-item has-feedback label="确认密码" prop="checkpass">
          <a-input-password v-model="userinfo.checkpass"></a-input-password>
        </a-form-model-item>
      </a-form-model>
    </a-modal>

	</div>
</template>

<script>
const columns = [
	{
    title: 'ID',
    dataIndex: 'ID',
    key: 'id',
    width: '10%',
		align:'center'
  },
	{
    title: '用户名',
    dataIndex: 'username',
    key: 'username',
    width: '20%',
		align:'center'
  },
	{
    title: '角色',
    dataIndex: 'role',
    key: 'role',
    width: '20%',
		scopedSlots:{customRender:'role'},
		align:'center'
  },
	{
    title: '操作',
    key: 'action',
    width: '30%',
		scopedSlots:{customRender:'action'},
		align:'center'
  },
]
export default {
	data(){
		return{
			columns,
			changePasswordVisible: false,
			// 分页
			pagination: {
        pageSizeOptions: ['5', '10', '20'],
        pageSize: 5,
        total: 0,
        showSizeChanger: true,
        showTotal: (total) => `共${total}条`,
      },
			queryParam: {
				username:'',
        pagesize: 5,
        pagenum: 1,
      },
			userlist:[],
			AddUserVisible:false,
			editUserVisible:false,
			userinfo:{
				id:0,
				username:'',
				password:'',
				checkpass:'',
				role:2
			},
			userRules:{
				username: [
          {
            validator: (rule, value, callback) => {
              if (this.userinfo.username == '') {
                callback(new Error('请输入用户名'))
              }
              if ([...this.userinfo.username].length < 4 || [...this.userinfo.username].length > 12) {
                callback(new Error('用户名应当在4到12个字符之间'))
              } else {
                callback()
              }
            },
            trigger: 'blur',
          },
        ],
        password: [
          {
            validator: (rule, value, callback) => {
              if (this.userinfo.password == '') {
                callback(new Error('请输入密码'))
              }
              if ([...this.userinfo.password].length < 6 || [...this.userinfo.password].length > 20) {
                callback(new Error('密码应当在6到20位之间'))
              } else {
                callback()
              }
            },
            trigger: 'blur',
          },
        ],
				checkpass: [
          {
            validator: (rule, value, callback) => {
              if (this.userinfo.checkpass == '') {
                callback(new Error('请输入密码'))
              }
              if (this.userinfo.password !== this.userinfo.checkpass) {
                callback(new Error('密码不一致,请重新输入'))
              } else {
                callback()
              }
            },
            trigger: 'blur',
          },
        ],



			},
		}
	},
	created(){
		this.getUserList()
	},
	computed: {
		// 加载前布尔判断
    IsAdmin: function () {
      if (this.userinfo.role === 1) {
        return true
      } else {
        return false
      }
    }
	},
	methods:{
		// 查询用户
		async getUserList(){
			const { data:res } = await this.$http.get('api/v1/user/', {
				params:{
					username: this.queryParam.username,
					size: this.queryParam.pagesize,
					page: this.queryParam.pagenum,
					},
				})
			if(res.code != 200)return this.$message.error(res.msg)
			this.userlist = res.data
			this.pagination.total = res.total
		},
		// 分页
		handleTableChange(pagination, filters, sorter) {
      var pager = { ...this.pagination }
      pager.current = pagination.current
      pager.pageSize = pagination.pageSize
      this.queryParam.pagesize = pagination.pageSize
      this.queryParam.pagenum = pagination.current

      if (pagination.pageSize !== this.pagination.pageSize) {
        this.queryParam.pagenum = 1
        pager.current = 1
      }
      this.pagination = pager
      this.getUserList()
    },
		// 删除用户
		async delUser(id){
		  const { data: res } = await this.$http.delete(`api/admin/user/${id}/`)
			if(res.code != 200)return this.$message.error(res.msg)
			this.queryParam.pagenum = 1
			this.$message.success('删除成功')
			this.getUserList()
		},

		AddUserOk(){
			this.$refs.addUserRef.validate(async (vaild)=>{
				if(!vaild)return this.$message.error("格式不正确")
				const { data: res } = await this.$http.post("api/v1/user/",{
					"username":this.userinfo.username,
					"password":this.userinfo.password,
					"role": this.userinfo.role,
					})
				if (res.code != 200) return this.$message.error(res.msg)
				// 清空搜索框
				this.$refs.addUserRef.resetFields()
				// 关闭弹窗
        this.AddUserVisible = false
        this.$message.success('添加用户成功')
        this.getUserList()
			})
		},
		// role布尔值判断
		adminChange(checked){
      if (checked) {
        this.userinfo.role = 1
      } else {
        this.userinfo.role = 2
      }
		},
		// 取消的方法
		addUserCanal(){
			this.$refs.addUserRef.resetFields()
			this.AddUserVisible = false
		},
		// 编辑框获取用户信息
		async editUser(id){
			this.editUserVisible = true
			const {data: res } = await this.$http.get(`/api/v1/user/${id}/`)
			this.userinfo = res.data
			this.userinfo.id = id
		},
		// 编辑用户
		editUserOk(){
			this.$refs.addUserRef.validate(async (vaild)=>{
				if(!vaild)return this.$message.error("格式不正确")
				const { data: res } = await this.$http.put(`/api/admin/user/${this.userinfo.id}/`,{
					"username":this.userinfo.username,
					"role": this.userinfo.role,
					})
				if (res.code != 200) return this.$message.error(res.msg)
				// 清空搜索框
				this.$refs.addUserRef.resetFields()
				// 关闭弹窗
        this.editUserVisible = false
        this.$message.success('编辑用户成功')
        this.getUserList()
			})
		},
		// 取消编辑销毁数据
		editUserCanal(){
			this.$refs.addUserRef.resetFields()
			this.editUserVisible = false
		},
		// 修改密码获取信息
		ChangePassword(id){
			this.changePasswordVisible = true
      this.userinfo.id = id
		},
		// 修改密码
		changePasswordOk() {
      this.$refs.addUserRef.validate(async (valid) => {
        if (!valid) return this.$message.error('参数不符合要求,请重新输入')
        const { data: res } = await this.$http.put(`/api/admin/editpass/${this.userinfo.id}/`,{
				"password":this.userinfo.password
			})
        if (res.code != 200) return this.$message.error(res.msg)
        this.changePasswordVisible = false
        this.$message.success('修改密码成功')
        this.getUserList()
      })
    },
		// 修改密码取消
		changePasswordCancel() {
      this.$refs.addUserRef.resetFields()
      this.changePasswordVisible = false
      this.$message.info('已取消')
    },


	}

}
</script>

<style>
.actionSlot{
	display: flex;
	justify-content: center;
}

</style>

分类和文章列表页

CV大法好,只是在后端新增了一个搜索文章的功能。和搜索用户也是一样的啦。

model/Article.go

// SearchArticle 搜索文章标题
func SearchArticle(title string, pageSize int, pageNum int) ([]Article, int, int64) {
	var articleList []Article
	var err error
	var total int64
	err = Db.Select("article.id,title, img, created_at, updated_at, `desc`, comment_count, read_count, category.name").Limit(pageSize).Offset((pageNum-1)*pageSize).Order("Created_At DESC").Joins("Category").Where("title LIKE ?",
		title+"%",
	).Find(&articleList).Error
	// 单独计数
	Db.Model(&articleList).Count(&total)

	if err != nil {
		return nil, errmsg.ERROR, 0
	}
	return articleList, errmsg.SUCCSE, total
}

/api/v1/article.go

// 查询文章列表
func GetArticles(c *gin.Context)  {
	// 字符串转int
	Size,_ := strconv.Atoi(c.DefaultQuery("size","3"))
	Page,_ := strconv.Atoi(c.DefaultQuery("page","1"))
	title := c.Query("title")
	if len(title) == 0 {
		data, code, total := model.GetArticles(Size, Page)
		c.JSON(http.StatusOK, gin.H{
			"code":  code,
			"msg":   errmsg.GetErrorMsg(code),
			"data":  data,
			"total": total,
		})
		return
	}
	data, code, total := model.SearchArticle(title,Size,Page)
	c.JSON(http.StatusOK, gin.H{
		"code":  code,
		"data":    data,
		"total":   total,
		"msg": errmsg.GetErrorMsg(code),
	})

}

前端页面也放一下代码,闭着眼复制就可以了。

components/article/ArtList.vue

<template>
  <div>
    <a-card>
      <a-row :gutter="20">
        <a-col :span="6">
          <a-input-search
            v-model="queryParam.title"
            placeholder="输入文章名查找"
            enter-button
            allowClear
            @search="getArtList"
          />
        </a-col>
        <a-col :span="4">
          <a-button type="primary" @click="$router.push('addart')">新增</a-button>
        </a-col>

        <a-col :span="3">
          <a-select placeholder="请选择分类" style="width: 200px" @change="CateChange">
            <a-select-option
              v-for="item in Catelist"
              :key="item.id"
              :value="item.id"
            >{{ item.name }}</a-select-option>
          </a-select>
        </a-col>
        <a-col :span="1">
          <a-button type="info" @click="getArtList()">显示全部</a-button>
        </a-col>
      </a-row>

      <a-table
        rowKey="ID"
        :columns="columns"
        :pagination="pagination"
        :dataSource="Artlist"
        bordered
        @change="handleTableChange"
      >
        <span class="ArtImg" slot="img" slot-scope="img">
          <img :src="img" />
        </span>
        <template slot="action" slot-scope="data">
          <div class="actionSlot">
            <a-button
              size="small"
              type="primary"
              icon="edit"
              style="margin-right: 15px"
              @click="$router.push(`addart/${data.ID}`)"
            >编辑</a-button>
						<a-popconfirm title="确认删除?删除后无法恢复" ok-text="确认" cancel-text="取消" @confirm="deleteArt(data.ID)">
				  		<a-button icon="delete" size="small" type="danger">删除</a-button>
						</a-popconfirm>
          </div>
        </template>
      </a-table>
    </a-card>
  </div>
</template>

<script>
import day from 'dayjs'

const columns = [
  {
    title: 'ID',
    dataIndex: 'ID',
    width: '5%',
    key: 'id',
    align: 'center',
  },
  {
    title: '更新日期',
    dataIndex: 'UpdatedAt',
    width: '10%',
    key: 'UpdatedAt',
    align: 'center',
    customRender: (val) => {
      return val ? day(val).format('YYYY年MM月DD日 HH:mm') : '暂无'
    },
  },
  {
    title: '分类',
    dataIndex: 'Category.name',
    width: '5%',
    key: 'name',
    align: 'center',
  },
  {
    title: '文章标题',
    dataIndex: 'title',
    width: '15%',
    key: 'title',
    align: 'center',
  },
  {
    title: '文章描述',
    dataIndex: 'desc',
    width: '20%',
    key: 'desc',
    align: 'center',
  },
  {
    title: '缩略图',
    dataIndex: 'img',
    width: '20%',
    key: 'img',
    align: 'center',
    scopedSlots: { customRender: 'img' },
  },
  {
    title: '操作',
    width: '15%',
    key: 'action',
    align: 'center',
    scopedSlots: { customRender: 'action' },
  },
]

export default {
  data() {
    return {
      pagination: {
        pageSizeOptions: ['5', '10', '20'],
        pageSize: 5,
        total: 0,
        showSizeChanger: true,
        showTotal: (total) => `共${total}条`,
      },
      Artlist: [],
      Catelist: [],
      columns,
      queryParam: {
        title: '',
        pagesize: 5,
        pagenum: 1,
      },
    }
  },
  created() {
		this.getCateList()
    this.getArtList()
  },
  methods: {
    // 获取文章列表
    async getArtList() {
      const { data: res } = await this.$http.get('api/v1/article/', {
        params: {
          title: this.queryParam.title,
          size: this.queryParam.pagesize,
          page: this.queryParam.pagenum,
        },
      })
      if (res.code !== 200) {
        if (res.code === 1004 || 1005 || 1006 || 1007) {
          window.sessionStorage.clear()
          this.$router.push('/login')
        }
        this.$message.error(res.message)
      }

      this.Artlist = res.data
      this.pagination.total = res.total
    },
    // 获取分类
    async getCateList() {
      const { data: res } = await this.$http.get('api/v1/category/',{params: {
          size: 100,
          page: 1,
        },})
      if (res.code !== 200) return this.$message.error(res.msg)
      this.Catelist = res.data
      this.pagination.total = res.total
    },
    // 更改分页
    handleTableChange(pagination, filters, sorter) {
      var pager = { ...this.pagination }
      pager.current = pagination.current
      pager.pageSize = pagination.pageSize
      this.queryParam.pagesize = pagination.pageSize
      this.queryParam.pagenum = pagination.current

      if (pagination.pageSize !== this.pagination.pageSize) {
        this.queryParam.pagenum = 1
        pager.current = 1
      }
      this.pagination = pager
      this.getArtList()
    },
    // 删除文章
    async deleteArt(id) {
			const { data: res } = await this.$http.delete(`api/admin/article/${id}/`)
			if (res.code != 200) return this.$message.error(res.msg)
			this.$message.success('删除成功')
			this.queryParam.pagenum = 1
			this.getArtList()

    },
    // 查询分类下的文章
    CateChange(value) {
      this.getCateArt(value)
    },
    async getCateArt(id) {
      const { data: res } = await this.$http.get(`api/v1/catelist/${id}/`, {
        params: { size: this.queryParam.pagesize, page: this.queryParam.pagenum },
      })
      if (res.code !== 200) return this.$message.error(res.msg)
      this.Artlist = res.data
      this.pagination.total = res.total
    },
  },
}
</script>

<style scoped>
.actionSlot {
  display: flex;
  justify-content: center;
}
.ArtImg {
  height: 100%;
  width: 100%;
}

.ArtImg img {
  width: 100px;
  height: 80px;
}
</style>

components/category/CateList.vue

<template>
	<div>
		<a-card>
			<a-row :gutter="20">
				<a-col :span="4">
					<a-button type="primary" @click="AddcateVisible = true">新增分类</a-button>
				</a-col>
			</a-row>
			<a-table
				:columns='columns' 
				rowKey='id'
				:pagination="pagination"
				:dataSource="catelist"
				bordered
				@change="handleTableChange"
			>
			<template slot="action" slot-scope="data">
				<div class="actionSlot">
				<a-button type="primary" icon="edit" style="margin-right:15px" @click="editcate(data.id)">编辑</a-button>
				<a-popconfirm title="确认删除?删除后无法恢复" ok-text="确认" cancel-text="取消" @confirm="delcate(data.id)">
				  <a-button icon="delete" type="danger">删除</a-button>
				</a-popconfirm>
			</div>
			</template>
			</a-table>
		</a-card>
		
		<!-- 新增分类对话框 -->
		<a-modal
      title="新增分类"
      :visible="AddcateVisible"
      @ok="AddcateOk"
      @cancel="addcateCanal"
			closable
    >
      <a-form-model :model="cateinfo" ref="addcateRef">
				<a-form-model-item label="分类名" prop="name">
					<a-input v-model="cateinfo.name"></a-input>
				</a-form-model-item>
			</a-form-model>
    </a-modal>

		<!-- 编辑分类 -->
		<a-modal
      title="编辑分类"
      :visible="editcateVisible"
      @ok="editcateOk"
			width="60%"
      @cancel="editcateCanal"
			closable
    >
      <a-form-model :model="cateinfo" ref="addcateRef">
				<a-form-model-item label="分类名" prop="name">
					<a-input v-model="cateinfo.name"></a-input>
				</a-form-model-item>
				
			</a-form-model>
    </a-modal>
	</div>
</template>

<script>
const columns = [
	{
    title: 'ID',
    dataIndex: 'id',
    key: 'id',
    width: '10%',
		align:'center'
  },
	{
    title: '分类名',
    dataIndex: 'name',
    key: 'name',
    width: '20%',
		align:'center'
  },
	{
    title: '操作',
    key: 'action',
    width: '30%',
		scopedSlots:{customRender:'action'},
		align:'center'
  },
]
export default {
	data(){
		return{
			columns,
			changePasswordVisible: false,
			// 分页
			pagination: {
        pageSizeOptions: ['5', '10', '20'],
        pageSize: 5,
        total: 0,
        showSizeChanger: true,
        showTotal: (total) => `共${total}条`,
      },
			queryParam: {
				name:'',
        pagesize: 5,
        pagenum: 1,
      },
			catelist:[],
			AddcateVisible:false,
			editcateVisible:false,
			cateinfo:{
				id:0,
				name:'',
			},
		}
	},
	created(){
		this.getcateList()
	},
	methods:{
		// 查询分类
		async getcateList(){
			const { data:res } = await this.$http.get('api/v1/category/', {
				params:{
					size: this.queryParam.pagesize,
					page: this.queryParam.pagenum,
					},
				})
			if(res.code != 200)return this.$message.error(res.msg)
			this.catelist = res.data
			this.pagination.total = res.total
		},
		// 分页
		handleTableChange(pagination, filters, sorter) {
      var pager = { ...this.pagination }
      pager.current = pagination.current
      pager.pageSize = pagination.pageSize
      this.queryParam.pagesize = pagination.pageSize
      this.queryParam.pagenum = pagination.current

      if (pagination.pageSize !== this.pagination.pageSize) {
        this.queryParam.pagenum = 1
        pager.current = 1
      }
      this.pagination = pager
      this.getcateList()
    },
		// 删除分类
		async delcate(id){
		  const { data: res } = await this.$http.delete(`api/admin/category/${id}/`)
			if(res.code != 200)return this.$message.error(res.msg)
			this.$message.success('删除成功')
			this.queryParam.pagenum = 1
			this.getcateList()
		},

		async AddcateOk(){
				const { data: res } = await this.$http.post("api/admin/category/",{
					"name":this.cateinfo.name,
					})
				if (res.code != 200) return this.$message.error(res.msg)
				// 清空搜索框
				this.$refs.addcateRef.resetFields()
				// 关闭弹窗
        this.AddcateVisible = false
        this.$message.success('添加成功')
        this.getcateList()
		},
		// 取消的方法
		addcateCanal(){
			this.$refs.addcateRef.resetFields()
			this.AddcateVisible = false
		},
		// 编辑框获取分类信息
		async editcate(id){
			this.editcateVisible = true
			const {data: res } = await this.$http.get(`/api/v1/category/${id}/`)
			this.cateinfo = res.data
			this.cateinfo.id = id
		},
		// 编辑分类
		async editcateOk(){
				const { data: res } = await this.$http.put(`/api/admin/category/${this.cateinfo.id}/`,{
					"name":this.cateinfo.name,
					})
				if (res.code != 200) return this.$message.error(res.msg)
				// 清空搜索框
				this.$refs.addcateRef.resetFields()
				// 关闭弹窗
        this.editcateVisible = false
        this.$message.success('编辑成功')
        this.getcateList()
		},
		// 取消编辑销毁数据
		editcateCanal(){
			this.$refs.addcateRef.resetFields()
			this.editcateVisible = false
		},
	}

}
</script>

<style>
.actionSlot{
	display: flex;
	justify-content: center;
}

</style>