数据列表

直接使用element+的列表组件

  1. <template>
  2. <el-table :data="tableData" border style="width: 100%">
  3. <el-table-column prop="id" label="编号" />
  4. <el-table-column prop="username" label="帐号" />
  5. <el-table-column prop="nickName" label="姓名" />
  6. <el-table-column prop="email" label="邮箱" />
  7. <el-table-column prop="createTime" label="添加时间" />
  8. <el-table-column prop="loginTime" label="最后登录" />
  9. <el-table-column prop="status" label="是否启用" />
  10. <el-table-column label="操作" />
  11. </el-table>
  12. </template>

定义接口获取数据

  1. //获取用户数据列表的接口
  2. interface AdminListParams {
  3. keyword: string;
  4. pageNum: Number;
  5. pageSize: Number
  6. }
  7. //获取用户数据列表
  8. export const getAdminList = (data :AdminListParams):PromiseRes<{list: {}[]}> => request.get('/admin/list',{params: data})

因为我们只用到返回数据的list,因此在返回值类型的定义只写list为对象数组格式即可
获取并渲染数据

  1. <script setup lang='ts'>
  2. import { reactive,toRefs } from 'vue';
  3. import {getAdminList} from '../../request/api'
  4. const state = reactive<{tableData: {}[]}>({
  5. tableData: []
  6. })
  7. const {tableData} = toRefs(state);
  8. getAdminList({
  9. keyword: '',
  10. pageSize: 10,
  11. pageNum: 1
  12. }).then(res => {
  13. if(res.code === 200) {
  14. tableData.value = res.data.list
  15. }
  16. })
  17. </script>

格式化时间

使用第三方库,打包的时候体积太大
推荐day.js,体积小,核心功能都有
但是能自己封装组件才是最优选择

先写格式化时间函数

  1. //补0函数
  2. const addZero = (num: number) => {
  3. return num < 10? '0'+num : num
  4. }
  5. //格式化时间
  6. const formatDate = (time?: string) => {
  7. if(!time) return '';
  8. let date = new Date(time);
  9. let year = addZero(date.getFullYear());
  10. let month = addZero(date.getMonth()+1);
  11. let day = addZero(date.getDay());
  12. let hours = addZero(date.getHours());
  13. let minutes = addZero(date.getMinutes());
  14. let seconds = addZero(date.getSeconds());
  15. return `${year}-${month}-${day} ${hours}-${minutes}-${seconds}`
  16. }

:::info 这里使用addZero时有个小技巧,year这些如果直接赋值为date.getFullYear(),则为number类型,但是在补0函数里的返回值有可能是string,因此直接一步到位,使用addZero之后再赋值,便可以自动转换类型。 :::

使用格式化函数

  1. <template>
  2. <el-table :data="tableData" border style="width: 100%">
  3. <el-table-column prop="id" label="编号" />
  4. <el-table-column prop="username" label="帐号" />
  5. <el-table-column prop="nickName" label="姓名" />
  6. <el-table-column prop="email" label="邮箱" />
  7. <el-table-column label="添加时间" >
  8. <template v-slot:default="scope">
  9. {{formatDate(scope.row.createTime)}}
  10. </template>
  11. </el-table-column>
  12. <el-table-column label="最后登录" >
  13. <template v-slot:default="scope">
  14. {{formatDate(scope.row.loginTime)}}
  15. </template>
  16. </el-table-column>
  17. <el-table-column prop="status" label="是否启用" />
  18. <el-table-column label="操作" />
  19. </el-table>
  20. </template>

是否启用

实质是一个开关,使用element+的switch组件

  1. <el-table-column label="是否启用" >
  2. <template v-slot:default="scope">
  3. <el-switch v-model="scope.row.status" :active-value="1" :inactive-value="0">
  4. </el-switch>
  5. </template>
  6. </el-table-column>

这里v-model绑定控制激活状态的变量

弹框组件

在操作这一栏,应该包含两种操作,“分配角色”和“编辑”
其实就是“增”和“改”
其效果都是弹出一个表单
组件内遇到可复用的模块,依然封装为组件

在ums文件夹下新建components文件夹存放ums菜单内的公共组件EditAdmin.vue
image.png
但是这个组件只能在执行点击操作后才展示,因此应该进行父子组件的通信
并且展示位置应该是在表单之外,与表单平级

  1. <EditAdmit :visible="visible" @close="closeDialog" @modifyAdmin="modifyAdmin"></EditAdmit>
  2. //点击编辑按钮
  3. const editAdmin = () => {
  4. visible.value = true;
  5. }
  6. //隐藏弹框
  7. const closeDialog = () => {
  8. visible.value = false;
  9. }
  10. //接受信息后关闭弹窗
  11. const modifyAdmin = () => {
  12. visible.value = false;
  13. }

其中visible控制其展示,close和modifyAdmin则是弹窗组件里点击取消和确定之后的事件

  1. <template>
  2. <el-dialog v-model="visible" title="Shipping address" :before-close="close">
  3. <el-form :model="newForm" :label-width="formLabelWidth">
  4. <el-form-item label="Promotion name">
  5. <el-input v-model="newForm.username" autocomplete="off" />
  6. </el-form-item>
  7. <el-form-item label="Zones">
  8. <el-select v-model="newForm.username" placeholder="Please select a zone">
  9. <el-option label="Zone No.1" value="shanghai" />
  10. <el-option label="Zone No.2" value="beijing" />
  11. </el-select>
  12. </el-form-item>
  13. </el-form>
  14. <template #footer>
  15. <span class="dialog-footer">
  16. <el-button @click="close">取消</el-button>
  17. <el-button type="primary" @click="modifyAdmin">
  18. 确定
  19. </el-button>
  20. </span>
  21. </template>
  22. </el-dialog>
  23. </template>
  1. <script setup lang='ts'>
  2. import { reactive, toRefs, watch } from 'vue';
  3. const props = defineProps<{ visible: boolean;form: {username: string} }>();
  4. const state = reactive<{formLabelWidth: string;newForm: {username?: string}}>({
  5. formLabelWidth: '120px',
  6. newForm: {}
  7. })
  8. const { formLabelWidth,newForm } = toRefs(state)
  9. watch(() => props.form,() => {
  10. newForm.value = {...props.form}
  11. })
  12. const emit = defineEmits<{(event: 'close' ): void}>();
  13. //自定义事件 关闭弹窗表单
  14. const close = () => {
  15. emit('close')
  16. }
  17. //自定义事件 修改弹窗表单
  18. const modifyAdmin = () => {
  19. emit('modifyAdmin')
  20. }
  21. </script>

如果点击弹窗组件的外面,弹窗组件依然会关闭,这是组件封装的默认行为。但是再次点击编辑按钮就发现,无法顺利开启弹窗组件了。因此必须对弹窗自带的关闭时间进行拦截

  1. <el-dialog v-model="visible" title="Shipping address" :before-close="close">

:before-close=”close” 使得点击弹窗组件外面的行为会被拦截,随后触发组件里的close事件

传递所在行数据

最终弹窗组件接受的数据来自父组件,也就是用户点击表单的那一行

  1. <el-table-column label="操作">
  2. <template #default="{row}">
  3. <el-button text>
  4. 分配角色
  5. </el-button>
  6. <el-button @click="editAdmin(row)" text>
  7. 编辑
  8. </el-button>
  9. </template>
  10. </el-table-column>

结构出row
本地设置变量rowData用于记录数据
image.png

在点击事件里赋值

  1. const editAdmin = (row :{}) => {
  2. rowData.value = row;
  3. visible.value = true;
  4. }

通过props传递

  1. <EditAdmit :visible="visible" :form="rowData" @close="closeDialog" @modifyAdmin="modifyAdmin"></EditAdmit>

浅拷贝对象

直接使用父组件传过来的rowData,发现数据无法渲染。因为setup钩子的原因。
此时使用watch监听props.rowData

  1. const props = defineProps<{ visible: boolean;form: {username: string} }>();
  2. const state = reactive<{formLabelWidth: string;newForm: {username?: string}}>({
  3. formLabelWidth: '120px',
  4. newForm: {}
  5. })
  6. const { formLabelWidth,newForm } = toRefs(state)
  7. watch(() => props.form,() => {
  8. newForm.value = {...props.form}
  9. })

:::info 使用浅拷贝的原因:
会发现子组件里修改了rowData之后,点击“取消”,父组件里的内容也一样会跟着被修改。
因为props传递的数据的对象,对象里都是基础数据,需要进行浅拷贝.
newForm.value = {…props.form} ::: watch在监听对象数据时,需要使用函数得到返回值

  1. watch(() => props.form,() => {
  2. newForm.value = {...props.form}
  3. })

定义用户对象类型

src下新建types文件夹,里面再创建admin.d.ts,存放admin组件的接口类型

  1. interface AdminObjItf {
  2. username?: string
  3. }

因为父组件跟子组件里传递的rowData对象里有很多变量,其格式应该统一,写公共接口很方便使用

更新用户信息

api.ts

  1. //修改用户信息
  2. export const updateAdmin = (id:number, data :AdminObjItf):PromiseRes => request.get('/admin/update/'+id,{params: data})

因为不需要返回值,因此这里的PromsieRes不需要定义返回值类型。
此时会报错

  1. //定义Promise类型
  2. type PromiseRes<T = {}> = Promise<ManageResult<T>>

给T一个空对象即可

  1. //确定事件
  2. const modifyAdmin = () => {
  3. updateAdmin(newForm.value.id as number,newForm.value).then(
  4. res => {
  5. if(res.code === 200){
  6. close('reload')
  7. }
  8. }
  9. )
  10. }
  1. //获取最新表格数据
  2. const fetchData = () => {
  3. getAdminList({
  4. keyword: '',
  5. pageSize: 10,
  6. pageNum: 1
  7. }).then(res => {
  8. if (res.code === 200) {
  9. tableData.value = res.data.list
  10. }
  11. })
  12. }
  13. fetchData()
  1. //隐藏弹框
  2. const closeDialog = (r?: string) => {
  3. visible.value = false;
  4. rowData.value = {};
  5. if (r === 'reload'){
  6. fetchData()
  7. }
  8. }

如果自定义事件里有参数,且值为reload,则发请求更新表单

添加用户时间

如果需要“添加”功能,则rowData只需要传递空对象即可
而子组件里发请求的时候不带id,就能知道是添加功能