• 本地数据库IndexedDB - 学员管理系统之列表管理(二)


     IndexedDB是浏览器提供的本地数据库,它可以被网页脚本创建和操作。IndexedDB允许存储大量数据,提供查找接口,还能建立索引。这些都是LocalStorage或Cookie不具备的。就数据库类型而言,IndexedDB不属于关系型数据库(不支持SQL查询语句),更接近NoSQL数据库。

            通过上一篇“本地数据库IndexedDB - 学员管理系统之登录(一)”,我们已完成了系统的登录功能,这一章节则完成相关列表管理功能,实现管理员列表、年级管理、班级管理、教师列表、学员列表等栏目的增删改查。

            由于这中间工作较忙,离下篇发布时间相隔较长,并且结合后期开发过程,对前期部分定义内容进行了修改,有些地方差异,大家可以重新翻看上一篇。

    上篇地址:本地数据库IndexedDB - 学员管理系统之登录(一)_觉醒法师的博客-CSDN博客

            

    一、管理员列表

            在开始前,我们将上一章节的用户登录信息再修复一下;当用户登录成功后,会将信息缓存到localStorage中,本地状态管理器中也会只在一份;用户列表管理时,遇到登录用户信息,需要判断是否可以修改或删除等,需要从store中获取用户信息,但是页面刷新后,store状态中的用户信息会丢失,这时我们在校验Token有效情况下,需要重新将缓存中的用户信息,保存到store状态中。打开store/actions.js文件,添加以下代码:

    1. import Vue from 'vue'
    2. import { USERINFO, TOKEN } from './mutationsType'
    3. import { tokenIsFailure } from '@/api'
    4. const actions = {
    5. /**
    6. * 重新加载缓存中用户信息
    7. */
    8. reloadUserInfo({commit}){
    9. let token = Vue.ls.get(TOKEN),
    10. userInfo = Vue.ls.get(USERINFO);
    11. if(token) {
    12. commit(TOKEN, token);
    13. }
    14. if(userInfo) {
    15. commit(USERINFO, userInfo);
    16. }
    17. },
    18. //...
    19. }

    1.1 获取用户列表

            首先我们在db/model/user.js中,定义loadUserAllList用户加载所有用户信息;筛选用户功能可通过传入name进行筛选,获取所有数据时通过filter进行过滤下即可;另外这里我们先使用getAll()方法来获取所有数据,在后期中我们会讲解如何进行分页查询。代码如下:

    1. /**
    2. * 获取用户列表
    3. */
    4. export const loadUserAllList = name => {
    5. return new Promise((resolve, reject) => {
    6. //打开游标
    7. let {store} = openTransactionIndex(storeName);
    8. //获取所有数据
    9. let alls = store.getAll();
    10. alls.onerror = function(e){
    11. reject(
    12. rJson(0, e, '查询出错了~')
    13. );
    14. }
    15. alls.onsuccess = function(e){
    16. let result = e.target.result;
    17. if(result){
    18. let data = result.map(item => {
    19. delete item['password'];
    20. delete item['accesstoken'];
    21. return item;
    22. });
    23. //如果name值存在,则进行过滤
    24. if(name){
    25. data = data.filter(item => item.name.includes(name));
    26. }
    27. resolve(
    28. rJson(1, data, '获取成功~')
    29. )
    30. }else{
    31. reject(
    32. rJson(0, null, '未查询到数据~')
    33. )
    34. }
    35. // console.log('store', result);
    36. }
    37. });
    38. }

            第二步,在api/index.js中定义userAllList接口函数,代码如下:

    1. import { loadUserAllList } from '@/db/model/user'
    2. /**
    3. * 获取用户列表
    4. */
    5. export const userAllList = keyword => {
    6. return loadUserAllList(keyword);
    7. }

            html代码:

            样式部分:

    1. .index-wrap{
    2. padding: 20px;
    3. }
    4. .filter-wrap{
    5. display: table;
    6. width: 100%;
    7. .item{
    8. display: table-cell;
    9. vertical-align: middle;
    10. &.left{
    11. text-align: left;
    12. }
    13. //left end
    14. &.right{
    15. text-align: right;
    16. }
    17. //right end
    18. }
    19. }

            JS代码部分:

            以上准备工作做完后,我们页面就有用户数据了,如下图:

           大家可以观察到admin的多选项 和 删除 按钮都是禁用的,这里因为当前登录用户是无法删除自己的。如何实现的呢,删除按钮是通过store中取到的用户ID和列表渲染用户ID进行对比,判断为当前登录用户则禁用;多选功能则是通过selectable功能,与store中的用户ID进行对比来判断的。

            当然,现在列表数据是显示了,但是只有一条数据,无法通过条件进行筛选,这块等我们将新增功能完成后,再来操作。

    1.2 新增数据

            首先,我们在db/model/user.js中定义toggleUser函数,用于新增 或 编辑用户信息;当数据为新增时,先判断用户名是否已存在,如已存在返回错误信息,不存在直接新增为新用户;当数据为编辑时,先获取该用户原始数据,用原始数据和新数据进行合并,如果使用put保存,会覆盖掉之前未修改数据。

            当编辑时候,先通过get()方法获取该用户所有信息,再合并数据。

            当新增时候,先通过name索引判断该用户是否存在,存在则返回提示信息;不存在则直接添加。

    代码如下:

    1. /**
    2. * 增加 或 编辑用户信息
    3. */
    4. export const toggleUser = data => {
    5. return new Promise((resolve, reject) => {
    6. //打开游标
    7. let {store, index} = openTransactionIndex(storeName, 'name', CONST_READ.READWRITE);
    8. let res;
    9. //md5处理密码
    10. if(data['password']){
    11. data.password = hex_md5(data.password);
    12. }
    13. //用户ID存在,则为编辑
    14. if(data['id']&&data.id!=0){
    15. data['updatetime'] = new Date().getTime();
    16. //获取原数据
    17. res = store.get(data.id);
    18. res.onerror = function(e){
    19. reject(
    20. rJson(0, e, '查询出错了~')
    21. );
    22. }
    23. res.onsuccess = function(e){
    24. let result = e.target.result;
    25. if(result){
    26. //合并数据,并保存
    27. res = store.put(Object.assign(result, data));
    28. res.onerror = function(e){
    29. reject(
    30. rJson(0, e, '查询出错了~')
    31. );
    32. }
    33. res.onsuccess = function(){
    34. resolve(
    35. rJson(1, null, '保存成功~')
    36. )
    37. }
    38. }else{
    39. reject(
    40. rJson(0, e, '用户数据不存在~')
    41. );
    42. }
    43. }
    44. }
    45. //新增(需要判断用户名是否已存在)
    46. else{
    47. //通过索引 获取用户名,判断用户名是否已存在
    48. res = index.getKey(data.name);
    49. res.onerror = function(e){
    50. reject(
    51. rJson(0, e, '查询出错了~')
    52. );
    53. }
    54. res.onsuccess = function(e){
    55. let result = e.target.result;
    56. //如果用户名已存在,返回错误信息
    57. if(result){
    58. reject(
    59. rJson(0, e, '该用户名已存在~')
    60. );
    61. }
    62. //用户名不存在,则直接添加
    63. else{
    64. data['createtime'] = new Date().getTime();
    65. data['updatetime'] = new Date().getTime();
    66. res = store.add(data);
    67. res.onerror = function(e){
    68. reject(
    69. rJson(0, e, '查询出错了~')
    70. );
    71. }
    72. res.onsuccess = function(){
    73. resolve(
    74. rJson(1, null, '保存成功~')
    75. )
    76. }
    77. }
    78. //if 2 end
    79. }
    80. //索引 End
    81. }
    82. //if end
    83. });
    84. }

           api/index.js添加addUserInfo()方法,代码如下:

    1. import { toggleUser } from '@/db/model/user'
    2. /**
    3. * 添加用户信息
    4. */
    5. export const addUserInfo = param => {
    6. return toggleUser(param);
    7. }

            接下来,我们来创建新增弹框组件,在components目录中创建UserDailog目录,再新增index.vue。

            弹框页面html代码部分:

            JS代码部分:

            新增弹框组件创建完成后,我们在列表页(pages/mange/index.vue)中引入该组件,在打开新增用户弹框前,将selectId 置为0表示为新增。代码如下:

    1. import UserDailog from '@/components/UserDailog'
    2. export default {
    3. data () {
    4. return {
    5. //选择ID
    6. selectId: 0,
    7. //查询关键字
    8. keyword: "",
    9. /**
    10. * 是否显示弹框
    11. */
    12. isShowDialog: false,
    13. /**
    14. * 列表数据
    15. */
    16. tableList: [],
    17. /**
    18. * 选的项
    19. */
    20. multipleSelection: []
    21. }
    22. },
    23. components: {
    24. UserDailog
    25. },
    26. //...
    27. methods: {
    28. /**
    29. * 关闭弹框
    30. */
    31. closeChange(){
    32. this.isShowDialog = false;
    33. },
    34. /**
    35. * 新增用户信息
    36. */
    37. addUserEvent(){
    38. this.selectId = 0;
    39. this.isShowDialog = true;
    40. },
    41. //...
    42. }
    43. }

            列表html部分引入组件,代码如下:

    1. <UserDailog :visible="isShowDialog"
    2. :uid="selectId"
    3. @closeChange="closeChange"
    4. @saveSuccessChange="updateUserList"
    5. >UserDailog>

            并在新增按钮添加打开弹框事件,代码如下:

    "small" type="primary" @click="addUserEvent">新增

            此时,我们可以看到新增弹框界面了,如下图:

             现在可以添加多条数据,进行筛选操作了。如添加test1、test2、test3、tom1、tom2,创建完后列表如下图:

             当我们在账号输入框中输入test,点击查询则只会显示包含test的账号,如下图:

            在账号输入框中输入tom,点击查询则只会显示包含tom的账号,如下图:

     

             这里列表查询、条件筛选和新增用户功能则完成了,接下来我们需要来完成修改数据功能。

    1.3 修改数据

            在“1.2 新增数据”中,我们已经完成了db/model/user.js中的编辑部分的代码功能,这里我们只需要在pages/mange/index.vue中添加打开编辑弹框,并传入选中数据ID。代码如下:

    1. methods: {
    2. /**
    3. * 编辑用户信息
    4. * @param {Object} id
    5. */
    6. editUserEvent(id){
    7. this.selectId = id;
    8. this.isShowDialog = true;
    9. },
    10. }

            当弹框为编辑状态时,需要获取当前用户的信息,所在在db/model/user.js中需要添加通过ID获取用户信息的功能函数,直接通过get()函数获取指定用户信息即可。代码如下:

    1. /**
    2. * 通过ID获取用户信息
    3. */
    4. export const getUserInfoById = id => {
    5. return new Promise((resolve, reject) => {
    6. //打开游标
    7. let {store} = openTransactionIndex(storeName);
    8. //通过ID获取数据
    9. let data = store.get(id);
    10. data.onerror = function(e){
    11. reject(
    12. rJson(0, e, '查询出错了~')
    13. );
    14. }
    15. data.onsuccess = function(e){
    16. let result = e.target.result;
    17. if(result){
    18. delete result['accesstoken'];
    19. resolve(
    20. rJson(1, result, '获取成功~')
    21. )
    22. }else{
    23. reject(
    24. rJson(0, e, '数据不存在~')
    25. );
    26. }
    27. }
    28. });
    29. }

            api/index.js中添加编辑和获取用户信息接口,代码如下:

    1. import { toggleUser, getUserInfoById } from '@/db/model/user'
    2. /**
    3. * 获取用户信息
    4. */
    5. export const getUserById = id => {
    6. return getUserInfoById(id);
    7. }
    8. /**
    9. * 编辑用户信息
    10. */
    11. export const editUserInfo = param => {
    12. return toggleUser(param);
    13. }

            编辑的时候,不是所有信息都是必填的,其实在“1.2 新增数据”的JS部分代码中,已经完成此部分功能。用户的密码在修改时,是非必填的,只有填入新密码时才会交验;另外修改用户信息,需要输入原密码 进行校验,校验错误进无法进行保存。

            此时UserDialog/index.vue中的JS部分修改如下:

    1. import { getUserById, addUserInfo, editUserInfo } from '@/api'
    2. import { hex_md5 } from '@/utils/md5'
    3. export default {
    4. props: {
    5. //用户ID
    6. uid: {
    7. type: Number,
    8. default: () => 0
    9. },
    10. visible: {
    11. type: Boolean,
    12. default: () => false
    13. }
    14. },
    15. data(){
    16. var validateUsername = (rule, value, callback) => {
    17. if (value === '') {
    18. callback(new Error('请输入用户名'));
    19. } else {
    20. callback();
    21. }
    22. };
    23. var validatePhone = (rule, value, callback) => {
    24. if (value === '') {
    25. callback(new Error('请输入手机号'));
    26. } else if(!/^1[0-9]{10}$/.test(value)){
    27. callback(new Error('请输入正确的手机号'));
    28. } else {
    29. callback();
    30. }
    31. };
    32. var validatePass = (rule, value, callback) => {
    33. if(this.uid == 0){
    34. if (value === '') {
    35. callback(new Error('请输入密码'));
    36. } else if(value.length < 6){
    37. callback(new Error('密码不能小于6位'));
    38. } else {
    39. if (this.form.checkPass !== '') {
    40. this.$refs.ruleForm.validateField('checkPass');
    41. }
    42. callback();
    43. }
    44. }else if(this.form.password){
    45. if (value === '') {
    46. callback(new Error('请输入密码'));
    47. } else if(value.length < 6){
    48. callback(new Error('密码不能小于6位'));
    49. } else {
    50. if (this.form.checkPass !== '') {
    51. this.$refs.ruleForm.validateField('checkPass');
    52. }
    53. callback();
    54. }
    55. }else{
    56. callback();
    57. }
    58. };
    59. var validatePass2 = (rule, value, callback) => {
    60. if (value === '') {
    61. callback(new Error('请再次输入密码'));
    62. } else if (value !== this.form.password) {
    63. callback(new Error('两次输入密码不一致!'));
    64. } else {
    65. callback();
    66. }
    67. };
    68. var validateOldPwd = (rule, value, callback) => {
    69. if (value === '') {
    70. callback(new Error('请输入原密码'));
    71. } else if (hex_md5(value) !== this.oldPassword) {
    72. callback(new Error('密码错误!'));
    73. } else {
    74. callback();
    75. }
    76. };
    77. return {
    78. oldPassword: "", //编辑时,记录旧密码
    79. form: {
    80. name: "",
    81. password: "",
    82. checkPass: "",
    83. phone: "",
    84. opwd: ""
    85. },
    86. formLabelWidth: '80px',
    87. rules: {
    88. name: [
    89. { validator: validateUsername, trigger: 'blur' }
    90. ],
    91. phone: [
    92. { validator: validatePhone, trigger: 'blur' }
    93. ],
    94. password: [
    95. { validator: validatePass, trigger: 'blur' }
    96. ],
    97. checkPass: [
    98. { validator: validatePass2, trigger: 'blur' }
    99. ],
    100. opwd: [
    101. { validator: validateOldPwd, trigger: 'blur' }
    102. ]
    103. }
    104. }
    105. },
    106. watch: {
    107. uid(){
    108. if(this.uid!=0){
    109. this.updateUserInfo();
    110. }
    111. }
    112. },
    113. methods: {
    114. /**
    115. * 获取用户信息
    116. */
    117. updateUserInfo(){
    118. getUserById(this.uid).then(res => {
    119. if(res.code==1&&res['data']){
    120. this.form['name'] = res.data.name;
    121. this.form['phone'] = res.data.phone;
    122. this.oldPassword = res.data.password;
    123. }
    124. }).catch(e => {
    125. this.$message.error(e.msg);
    126. this.closeEvent();
    127. // console.error(e);
    128. })
    129. },
    130. /**
    131. * 获取提交的数据
    132. */
    133. getParam(){
    134. let data = {};
    135. if(this.form['name']){
    136. data['name'] = this.form.name;
    137. }
    138. if(this.form['password']){
    139. data['password'] = this.form.password;
    140. }
    141. if(this.form['phone']){
    142. data['phone'] = this.form.phone;
    143. }
    144. return data;
    145. },
    146. /**
    147. * 添加用户信息
    148. */
    149. addUserInfo(){
    150. addUserInfo(this.getParam()).then(() => {
    151. this.$emit('saveSuccessChange', {});
    152. this.closeEvent();
    153. }).catch(e => {
    154. this.$message.error(e.msg);
    155. });
    156. },
    157. /**
    158. * 编辑用户信息
    159. */
    160. editUserInfo(){
    161. editUserInfo({
    162. id: this.uid,
    163. ...this.getParam()
    164. }).then(() => {
    165. this.$emit('saveSuccessChange', {});
    166. this.closeEvent();
    167. }).catch(e => {
    168. this.$message.error(e.msg);
    169. });
    170. },
    171. /**
    172. * 提交表单
    173. */
    174. submitForm(){
    175. this.$refs['ruleForm'].validate((valid) => {
    176. if(valid){
    177. //新增用户
    178. if(this.uid==0){
    179. this.addUserInfo();
    180. }
    181. //编辑用户
    182. else{
    183. this.editUserInfo();
    184. }
    185. }else{
    186. return false;
    187. }
    188. });
    189. },
    190. /**
    191. * 关闭事件
    192. */
    193. closeEvent(){
    194. this.$refs['ruleForm'].resetFields();
    195. this.$emit('closeChange', {});
    196. },
    197. }
    198. }

            编辑界面如下图:

            当我们需要修改用户名或手机号是,是必须输入原密码才能校验成功并进行保存。当密码框输入新密码后,则会显示再次确认密码框,不输入则非必填项。如将tom2修改为Peter,则列表则会更新为最新数据,如下图:

    1.4 删除某条数据

            删除单表数据,直接通过delete(’id‘)即可,在db/model/user.js中添加deleteUserById函数,代码如下:

    1. /**
    2. * 通过ID删除用户信息
    3. */
    4. export const deleteUserById = id => {
    5. return new Promise((resolve, reject) => {
    6. //打开游标
    7. let {store} = openTransactionIndex(storeName, undefined, CONST_READ.READWRITE);
    8. //删除指定用户信息
    9. let res = store.delete(id);
    10. res.onerror = function(e){
    11. reject(
    12. rJson(0, e, '操作出错了~')
    13. );
    14. }
    15. res.onsuccess = function(){
    16. resolve(
    17. rJson(1, null, '删除成功~')
    18. )
    19. }
    20. });
    21. }

            api/index.js添加deleteUserInfo函数,代码如下:

    1. import { deleteUserById } from '@/db/model/user'
    2. /**
    3. * 删除用户信息
    4. */
    5. export const deleteUserInfo = id => {
    6. return deleteUserById(id);
    7. }

            pages/mange/index.vue中添加删除方法,代码如下:

    1. methods: {
    2. /**
    3. * 删除用户信息
    4. * @param {Object} id
    5. */
    6. deleteEvent(id){
    7. this.$confirm('确认要删除该用户吗?', '提示', {
    8. confirmButtonText: '删除',
    9. cancelButtonText: '取消',
    10. type: 'warning'
    11. }).then(res => {
    12. deleteUserInfo(id).then(() => {
    13. this.$message.success('删除成功!');
    14. this.updateUserList();
    15. }).catch(e => {
    16. this.$message.error(e.msg);
    17. })
    18. }).catch(() => {
    19. this.$message({
    20. type: 'info',
    21. message: '已取消删除'
    22. });
    23. });
    24. }
    25. }

            给表格中的删除按钮绑定删除事件,代码如下:

    1. "danger"
    2. size="mini"
    3. :disabled="userInfo.id==scope.row.id"
    4. icon="el-icon-delete"
    5. circle
    6. @click="deleteEvent(scope.row.id)"
    7. >

            删除成功后,调用updateUserList()函数刷新数据列表即可。

    1.5 删除选中多条数据

            删除多项则需要使用到游标相关知识点了,在db/model/user.js中添加deleteUserByIds函数,用来删除多条数据记录。通过openCursor()函数,对用户表进行遍历,通过includes判断遍历数据ID是否在删除数组中,在则直接调用delete()函数(注:这里不需要指定唯一ID,当前游标指向当前遍历数据实例对象)。

    1. /**
    2. * 通过ID删除多条用户信息
    3. */
    4. export const deleteUserByIds = ids => {
    5. return new Promise((resolve, reject) => {
    6. if(Array.isArray(ids)){
    7. //打开游标
    8. let {store} = openTransactionIndex(storeName, undefined, CONST_READ.READWRITE);
    9. //打开游标
    10. let cursor = store.openCursor();
    11. cursor.onerror = function(e){
    12. reject(
    13. rJson(0, e, '操作出错了~')
    14. );
    15. }
    16. cursor.onsuccess = function(e){
    17. let result = e.target.result;
    18. if(result){
    19. if(ids.includes(result.key)){
    20. result.delete();
    21. }
    22. result.continue();
    23. }else{
    24. resolve(
    25. rJson(1, null, '删除成功~')
    26. )
    27. }
    28. }
    29. }else{
    30. reject(
    31. rJson(0, e, '请传入数组形式ID数据~')
    32. );
    33. }
    34. //end
    35. });
    36. }

            api/index.js中添加删除多项数据功能函数,代码如下:

    1. import { deleteUserByIds } from '@/db/model/user'
    2. /**
    3. * 删除选择中项的用户信息
    4. */
    5. export const deleteUserChecked = ids => {
    6. return deleteUserByIds(ids);
    7. }

            pages/mange/index.vue中添加删除多项数据的方法,代码如下:

    1. methods: {
    2. /**
    3. * 删除选中的ID项
    4. */
    5. deleteSelectedUser(){
    6. if(this.multipleSelection.length==0){
    7. this.$message({
    8. type: 'info',
    9. message: '请选择删除项'
    10. });
    11. return;
    12. }
    13. this.$confirm('确认要删除选中的用户吗?', '提示', {
    14. confirmButtonText: '删除',
    15. cancelButtonText: '取消',
    16. type: 'warning'
    17. }).then(res => {
    18. deleteUserChecked(this.multipleSelection.map(item => item.id)).then(() => {
    19. this.$message.success('删除成功!');
    20. this.updateUserList();
    21. }).catch(e => {
    22. this.$message.error(e.msg);
    23. })
    24. }).catch(() => {
    25. this.$message({
    26. type: 'info',
    27. message: '已取消删除'
    28. });
    29. });
    30. }
    31. }

            再给多项删除按钮添加绑定事件,代码如下:

    "small" type="danger" @click="deleteSelectedUser">删除

            到此为止,用户管理的增删除改查功能就全部完成了;这里讲解比较碎片化,如果不清楚地方,可以留言询问;如有不足之处,欢迎指出。

    二、年级管理

            年级管理的增删改查就不细讲了,增删改查也不复杂,可以按照“一、管理员列表” 中的功能进行复制,修改相应参数数据即可,都是大同小异。

           2.1 数据库操作文件

            创建db/model/grade.js,代码如下:

    1. import { DBIsLoadSuccess, openTransactionIndex, CONST_READ } from '@/db'
    2. import { rJson } from '@/utils/utils'
    3. let storeName = 'grade';
    4. /**
    5. * 获取年级列表
    6. */
    7. export const loadGradeAllList = name => {
    8. return new Promise((resolve, reject) => {
    9. //打开游标
    10. let {store} = openTransactionIndex(storeName);
    11. //获取所有数据
    12. let alls = store.getAll();
    13. alls.onerror = function(e){
    14. reject(
    15. rJson(0, e, '查询出错了~')
    16. );
    17. }
    18. alls.onsuccess = function(e){
    19. let result = e.target.result;
    20. if(result){
    21. let rData = result;
    22. if(name){
    23. rData = result.filter(item => item.name.includes(name));
    24. }
    25. resolve(
    26. rJson(1, rData, '获取成功~')
    27. )
    28. }else{
    29. reject(
    30. rJson(0, null, '未查询到数据~')
    31. )
    32. }
    33. // console.log('store', result);
    34. }
    35. });
    36. }
    37. /**
    38. * 增加 或 编辑 年级信息
    39. */
    40. export const toggleGrade = data => {
    41. return new Promise((resolve, reject) => {
    42. //打开游标
    43. let {store, index} = openTransactionIndex(storeName, 'name', CONST_READ.READWRITE);
    44. let res;
    45. //用户ID存在,则为编辑
    46. if(data['id']&&data.id!=0){
    47. data['updatetime'] = new Date().getTime();
    48. //获取原数据
    49. res = store.get(data.id);
    50. res.onerror = function(e){
    51. reject(
    52. rJson(0, e, '查询出错了~')
    53. );
    54. }
    55. res.onsuccess = function(e){
    56. let result = e.target.result;
    57. if(result){
    58. //合并数据,并保存
    59. res = store.put(Object.assign(result, data));
    60. res.onerror = function(e){
    61. reject(
    62. rJson(0, e, '查询出错了~')
    63. );
    64. }
    65. res.onsuccess = function(){
    66. resolve(
    67. rJson(1, null, '保存成功~')
    68. )
    69. }
    70. }else{
    71. reject(
    72. rJson(0, e, '年级不存在~')
    73. );
    74. }
    75. }
    76. }
    77. //新增(需要判断用户名是否已存在)
    78. else{
    79. //通过索引 获取用户名,判断用户名是否已存在
    80. res = index.getKey(data.name);
    81. res.onerror = function(e){
    82. reject(
    83. rJson(0, e, '查询出错了~')
    84. );
    85. }
    86. res.onsuccess = function(e){
    87. let result = e.target.result;
    88. //如果用户名已存在,返回错误信息
    89. if(result){
    90. reject(
    91. rJson(0, e, '该年级已存在~')
    92. );
    93. }
    94. //用户名不存在,则直接添加
    95. else{
    96. data['createtime'] = new Date().getTime();
    97. data['updatetime'] = new Date().getTime();
    98. res = store.add(data);
    99. res.onerror = function(e){
    100. reject(
    101. rJson(0, e, '查询出错了~')
    102. );
    103. }
    104. res.onsuccess = function(){
    105. resolve(
    106. rJson(1, null, '保存成功~')
    107. )
    108. }
    109. }
    110. }
    111. }
    112. });
    113. }
    114. /**
    115. * 通过ID删除 年级信息
    116. */
    117. export const deleteGradeById = id => {
    118. return new Promise((resolve, reject) => {
    119. //打开游标
    120. let {store} = openTransactionIndex(storeName, undefined, CONST_READ.READWRITE);
    121. //删除指定用户信息
    122. let res = store.delete(id);
    123. res.onerror = function(e){
    124. reject(
    125. rJson(0, e, '操作出错了~')
    126. );
    127. }
    128. res.onsuccess = function(){
    129. resolve(
    130. rJson(1, null, '删除成功~')
    131. )
    132. }
    133. });
    134. }
    135. /**
    136. * 通过ID删除多条 年级信息
    137. */
    138. export const deleteGradeByIds = ids => {
    139. return new Promise((resolve, reject) => {
    140. if(Array.isArray(ids)){
    141. //打开游标
    142. let {store} = openTransactionIndex(storeName, undefined, CONST_READ.READWRITE);
    143. //打开游标
    144. let cursor = store.openCursor();
    145. cursor.onerror = function(e){
    146. reject(
    147. rJson(0, e, '操作出错了~')
    148. );
    149. }
    150. cursor.onsuccess = function(e){
    151. let result = e.target.result;
    152. if(result){
    153. if(ids.includes(result.key)){
    154. result.delete();
    155. }
    156. result.continue();
    157. }else{
    158. resolve(
    159. rJson(1, null, '删除成功~')
    160. )
    161. }
    162. }
    163. }else{
    164. reject(
    165. rJson(0, e, '请传入数组形式ID数据~')
    166. );
    167. }
    168. });
    169. }

           2.2 api接口

            api/index.js中添加年级部分接口函数,代码如下:

    1. import {
    2. loadGradeAllList,
    3. toggleGrade,
    4. deleteGradeById,
    5. deleteGradeByIds
    6. } from '@/db/model/grade'
    7. /**
    8. * 获取年级列表
    9. */
    10. export const gradeAllList = keyword => {
    11. return loadGradeAllList(keyword);
    12. }
    13. /**
    14. * 添加 或 修改指定年级信息
    15. */
    16. export const toggleGradeInfo = name => {
    17. return toggleGrade(name);
    18. }
    19. /**
    20. * 删除指定年级
    21. */
    22. export const deleteGradeInfo = id => {
    23. return deleteGradeById(id);
    24. }
    25. /**
    26. * 删除指定多项年级信息
    27. */
    28. export const deleteGradeChecked = ids => {
    29. return deleteGradeByIds(ids);
    30. }

           2.3 列表页面

            打开年级列表页面pages/grade/index.vue,添加html、样式和JS部分功能。

            html代码部分:

            样式部分:

    1. .index-wrap{
    2. padding: 20px;
    3. }
    4. .filter-wrap{
    5. display: table;
    6. width: 100%;
    7. .item{
    8. display: table-cell;
    9. vertical-align: middle;
    10. &.left{
    11. text-align: left;
    12. }
    13. &.right{
    14. text-align: right;
    15. }
    16. }
    17. }

            JS部分:

    1. import { gradeAllList, toggleGradeInfo, deleteGradeInfo, deleteGradeChecked } from '@/api'
    2. import { formatDate } from '@/utils/utils'
    3. export default {
    4. data () {
    5. return {
    6. /**
    7. * 搜索关键词
    8. */
    9. keyword: "",
    10. /**
    11. * 列表数据
    12. */
    13. tableList: [],
    14. /**
    15. * 选中项
    16. */
    17. multipleSelection: []
    18. }
    19. },
    20. created() {
    21. this.updateList();
    22. },
    23. methods: {
    24. /**
    25. * 获取列表数据
    26. */
    27. updateList(){
    28. gradeAllList(this.keyword).then(res => {
    29. if(res.code==1&&Array.isArray(res.data)){
    30. this.tableList = res.data.map(item => {
    31. item['createtime'] = formatDate(item.createtime);
    32. item['updatetime'] = formatDate(item.updatetime);
    33. return item;
    34. });
    35. }
    36. }).catch(msg => {
    37. console.log('msg', msg);
    38. });
    39. //end
    40. },
    41. /**
    42. * 添加事件
    43. */
    44. addEvent(){
    45. this.$prompt('请输入年级名称', '提示', {
    46. confirmButtonText: '保存',
    47. cancelButtonText: '取消',
    48. inputValidator: function(value){
    49. if(value&&value.toString().length>0){
    50. return true;
    51. }else{
    52. return "请输入年级名称";
    53. }
    54. },
    55. inputErrorMessage: "请输入年级名称"
    56. }).then(({ value }) => {
    57. toggleGradeInfo({
    58. name: value
    59. }).then(res => {
    60. this.$message.success('添加成功');
    61. this.updateList();
    62. }).catch(e => {
    63. this.$message({
    64. type: 'info',
    65. message: e.msg
    66. });
    67. });
    68. }).catch(() => {
    69. this.$message({
    70. type: 'info',
    71. message: '已取消'
    72. });
    73. });
    74. },
    75. /**
    76. * 编辑事件
    77. */
    78. editEvent(id, name){
    79. this.$prompt('请输入年级名称', '提示', {
    80. confirmButtonText: '保存',
    81. cancelButtonText: '取消',
    82. inputValidator: function(value){
    83. if(value&&value.toString().length>0){
    84. return true;
    85. }else{
    86. return "请输入年级名称";
    87. }
    88. },
    89. inputValue: name,
    90. inputErrorMessage: "请输入年级名称"
    91. }).then(({ value }) => {
    92. toggleGradeInfo({
    93. id: id,
    94. name: value
    95. }).then(res => {
    96. this.$message.success('保存成功');
    97. this.updateList();
    98. }).catch(e => {
    99. this.$message({
    100. type: 'info',
    101. message: e.msg
    102. });
    103. });
    104. }).catch(() => {
    105. this.$message({
    106. type: 'info',
    107. message: '已取消'
    108. });
    109. });
    110. },
    111. /**
    112. * 删除事件
    113. * @param {Object} id
    114. */
    115. deleteEvent(id){
    116. this.$confirm('确认要删除该年级吗?', '提示', {
    117. confirmButtonText: '删除',
    118. cancelButtonText: '取消',
    119. type: 'warning'
    120. }).then(res => {
    121. deleteGradeInfo(id).then(() => {
    122. this.$message.success('删除成功!');
    123. this.updateList();
    124. }).catch(e => {
    125. this.$message.error(e.msg);
    126. })
    127. }).catch(() => {
    128. this.$message({
    129. type: 'info',
    130. message: '已取消删除'
    131. });
    132. });
    133. },
    134. /**
    135. * check 选中项项值
    136. * @param {Object} val
    137. */
    138. selectionChange(val){
    139. this.multipleSelection = val;
    140. },
    141. /**
    142. * 删除多项指定年级信息
    143. */
    144. deleteCheckedEvent(){
    145. if(this.multipleSelection.length==0){
    146. this.$message({
    147. type: 'info',
    148. message: '请选择删除项'
    149. });
    150. return;
    151. }
    152. this.$confirm('确认要删除选中的年级吗?', '提示', {
    153. confirmButtonText: '删除',
    154. cancelButtonText: '取消',
    155. type: 'warning'
    156. }).then(res => {
    157. deleteGradeChecked(this.multipleSelection.map(item => item.id)).then(() => {
    158. this.$message.success('删除成功!');
    159. this.updateList();
    160. }).catch(e => {
    161. this.$message.error(e.msg);
    162. })
    163. }).catch(() => {
    164. this.$message({
    165. type: 'info',
    166. message: '已取消删除'
    167. });
    168. });
    169. }
    170. //end
    171. }
    172. }

            到此为止,年级部分功能就完成了;由于这里添加信息只有年级名称,所以使用了Element的this.$prompt弹框进行添加和编辑。界面效果如下:

    列表页面:

    新增页面:

    三、教师列表

            这里先讲教师列表的功能实现,因为班级管理中需要关联对应班主任,这项为必填项;而教师对应的班级是非必填项。

            这部分和前面并无太大差异,还是常规的新删改查部分功能,所以也不作功能分解,直接按代码操作即可。

            3.1 数据库操作文件

            db/model/teacher.js代码如下:

    1. import { DBIsLoadSuccess, openTransactionIndex, CONST_READ } from '@/db'
    2. import { rJson } from '@/utils/utils'
    3. let storeName = 'teacher';
    4. /**
    5. * 获取 教师列表
    6. */
    7. export const loadTeacherAllList = name => {
    8. return new Promise((resolve, reject) => {
    9. //打开游标
    10. let {store} = openTransactionIndex(storeName);
    11. //获取所有数据
    12. let alls = store.getAll();
    13. alls.onerror = function(e){
    14. reject(
    15. rJson(0, e, '查询出错了~')
    16. );
    17. }
    18. alls.onsuccess = function(e){
    19. let result = e.target.result;
    20. if(result){
    21. let rData = result;
    22. if(name){
    23. rData = result.filter(item => item.name.includes(name));
    24. }
    25. resolve(
    26. rJson(1, rData, '获取成功~')
    27. )
    28. }else{
    29. reject(
    30. rJson(0, null, '未查询到数据~')
    31. )
    32. }
    33. // console.log('store', result);
    34. }
    35. });
    36. }
    37. /**
    38. * 获取教师信息
    39. */
    40. export const getTecharInfoById = id => {
    41. return new Promise((resolve, reject) => {
    42. //打开游标
    43. let {store} = openTransactionIndex(storeName);
    44. //通过ID获取数据
    45. let data = store.get(id);
    46. data.onerror = function(e){
    47. reject(
    48. rJson(0, e, '查询出错了~')
    49. );
    50. }
    51. data.onsuccess = function(e){
    52. let result = e.target.result;
    53. if(result){
    54. resolve(
    55. rJson(1, result, '获取成功~')
    56. )
    57. }else{
    58. reject(
    59. rJson(0, e, '数据不存在~')
    60. );
    61. }
    62. }
    63. });
    64. }
    65. /**
    66. * 增加 或 编辑 教师信息
    67. */
    68. export const toggleTeacher = data => {
    69. return new Promise((resolve, reject) => {
    70. //打开游标
    71. let {store, index} = openTransactionIndex(storeName, 'name', CONST_READ.READWRITE);
    72. let res;
    73. //ID存在,则为编辑
    74. if(data['id']&&data.id!=0){
    75. data['updatetime'] = new Date().getTime();
    76. //获取原数据
    77. res = store.get(data.id);
    78. res.onerror = function(e){
    79. reject(
    80. rJson(0, e, '查询出错了~')
    81. );
    82. }
    83. res.onsuccess = function(e){
    84. let result = e.target.result;
    85. if(result){
    86. //合并数据,并保存
    87. res = store.put(Object.assign(result, data));
    88. res.onerror = function(e){
    89. reject(
    90. rJson(0, e, '查询出错了~')
    91. );
    92. }
    93. res.onsuccess = function(){
    94. resolve(
    95. rJson(1, null, '保存成功~')
    96. )
    97. }
    98. }else{
    99. reject(
    100. rJson(0, e, '年级不存在~')
    101. );
    102. }
    103. }
    104. }
    105. //新增(需要判断是否已存在)
    106. else{
    107. //通过索引获取,判断是否已存在
    108. res = index.getKey(data.name);
    109. res.onerror = function(){
    110. reject(
    111. rJson(0, e, '查询出错了~')
    112. );
    113. }
    114. res.onsuccess = function(e){
    115. let result = e.target.result;
    116. //如果已存在,返回错误信息
    117. if(result){
    118. reject(
    119. rJson(0, e, '该教师已存在~')
    120. );
    121. }
    122. //不存在,则直接添加
    123. else{
    124. data['createtime'] = new Date().getTime();
    125. data['updatetime'] = new Date().getTime();
    126. res = store.add(data);
    127. res.onerror = function(e){
    128. reject(
    129. rJson(0, e, '查询出错了~')
    130. );
    131. }
    132. res.onsuccess = function(){
    133. resolve(
    134. rJson(1, null, '保存成功~')
    135. )
    136. }
    137. }
    138. //if 2 end
    139. }
    140. //索引 End
    141. }
    142. //if end
    143. });
    144. }
    145. /**
    146. * 通过ID删除教师信息
    147. */
    148. export const deleteTeacherById = id => {
    149. return new Promise((resolve, reject) => {
    150. //打开游标
    151. let {store} = openTransactionIndex(storeName, undefined, CONST_READ.READWRITE);
    152. //删除指定信息
    153. let res = store.delete(id);
    154. res.onerror = function(e){
    155. reject(
    156. rJson(0, e, '操作出错了~')
    157. );
    158. }
    159. res.onsuccess = function(){
    160. resolve(
    161. rJson(1, null, '删除成功~')
    162. )
    163. }
    164. });
    165. }
    166. /**
    167. * 通过ID删除多条教师信息
    168. */
    169. export const deleteTeacherByIds = ids => {
    170. return new Promise((resolve, reject) => {
    171. if(Array.isArray(ids)){
    172. //打开游标
    173. let {store} = openTransactionIndex(storeName, undefined, CONST_READ.READWRITE);
    174. //打开游标
    175. let cursor = store.openCursor();
    176. cursor.onerror = function(e){
    177. reject(
    178. rJson(0, e, '操作出错了~')
    179. );
    180. }
    181. cursor.onsuccess = function(e){
    182. let result = e.target.result;
    183. if(result){
    184. if(ids.includes(result.key)){
    185. result.delete();
    186. }
    187. result.continue();
    188. }else{
    189. resolve(
    190. rJson(1, null, '删除成功~')
    191. )
    192. }
    193. }
    194. }else{
    195. reject(
    196. rJson(0, e, '请传入数组形式ID数据~')
    197. );
    198. }
    199. });
    200. }

            3.2 api接口

            api/index.js文件添加以下代码:

    1. import {
    2. loadTeacherAllList,
    3. toggleTeacher,
    4. deleteTeacherById,
    5. deleteTeacherByIds,
    6. getTecharInfoById
    7. } from '@/db/model/teacher'
    8. /**
    9. * 获取教师列表
    10. */
    11. export const getTeachersList = name => {
    12. return loadTeacherAllList(name);
    13. }
    14. /**
    15. * 新增或修改教师信息
    16. */
    17. export const toggerTeacherInfo = params => {
    18. return toggleTeacher(params);
    19. }
    20. /**
    21. * 通过ID获取教师信息
    22. */
    23. export const loadTeacherById = id => {
    24. return getTecharInfoById(id);
    25. }
    26. /**
    27. * 单个教师信息删除
    28. */
    29. export const deleteTeacherInfo = id => {
    30. return deleteTeacherById(id);
    31. }
    32. /**
    33. * 删除指定多个教师信息
    34. */
    35. export const deleteSelectedTeacherInfo = ids => {
    36. return deleteTeacherByIds(ids);
    37. }

            3.3 新增页面

            在components中添加TeacherDialog/index.vue新增页面组件,html部分代码:

    js部分代码:

    添加界面如下:

            3.4 列表页面

            在pages目录中teacher/index.vue添加列表页面,html部分代码如下:

    js部分代码:

    列表界面如下:

    四、班级管理

            班级这部分由于关联了教师ID和年级ID,所以在做查询时,会稍微复杂点,同时需要之前的grade.js和teacher.js中增加单独查询功能,下面会一一阐述。

    4.1 数据库操作文件

            db/model/classify.js代码:

    1. import { DBIsLoadSuccess, openTransactionIndex, CONST_READ } from '@/db'
    2. import { rJson } from '@/utils/utils'
    3. import { getTeacherByIdsCursor } from './teacher.js'
    4. import { getGradeByIdsCursor } from './grade.js'
    5. let storeName = 'classify';
    6. /**
    7. * 获取 班级信息
    8. */
    9. export const loadClassAllList = async (name) => {
    10. return new Promise((resolve, reject) => {
    11. //打开游标
    12. let {store} = openTransactionIndex(storeName);
    13. //获取所有数据
    14. let alls = store.getAll();
    15. alls.onerror = function(e){
    16. console.log('error', e)
    17. reject(
    18. rJson(0, e, '查询出错了~')
    19. );
    20. }
    21. alls.onsuccess = function(e){
    22. let result = e.target.result;
    23. if(result){
    24. let rData = result;
    25. if(name){
    26. rData = result.filter(item => item.name.includes(name));
    27. }
    28. resolve(
    29. rJson(1, rData, '获取成功~')
    30. )
    31. }else{
    32. reject(
    33. rJson(0, null, '未查询到数据~')
    34. )
    35. }
    36. // console.log('store', result);
    37. }
    38. });
    39. }
    40. /**
    41. * 通过ID获取 班级信息
    42. */
    43. export const getClassInfoById = id => {
    44. return new Promise((resolve, reject) => {
    45. //打开游标
    46. let {store} = openTransactionIndex(storeName);
    47. //通过ID获取数据
    48. let data = store.get(id);
    49. data.onerror = function(e){
    50. reject(
    51. rJson(0, e, '查询出错了~')
    52. );
    53. }
    54. data.onsuccess = function(e){
    55. let result = e.target.result;
    56. if(result){
    57. resolve(
    58. rJson(1, result, '获取成功~')
    59. )
    60. }else{
    61. reject(
    62. rJson(0, e, '数据不存在~')
    63. );
    64. }
    65. }
    66. });
    67. }
    68. /**
    69. * 通过年级ID获取对应班级信息
    70. */
    71. export const getClassInfoByKey = key => {
    72. return new Promise((resolve, reject) => {
    73. //打开游标
    74. let {store, cursor} = openTransactionIndex(storeName, 'gid'),
    75. reData = [];
    76. cursor.onerror = function(e){
    77. reject(
    78. rJson(0, e, '查询出错了~')
    79. );
    80. }
    81. cursor.onsuccess = function(e){
    82. let result = e.target.result;
    83. if(result){
    84. if(result.key==key){
    85. reData.push({
    86. id: result.value.id,
    87. name: result.value.name
    88. });
    89. }
    90. result.continue();
    91. }else{
    92. resolve(
    93. rJson(1, reData, '获取成功~')
    94. )
    95. // console.log('end')
    96. }
    97. }
    98. });
    99. }
    100. /**
    101. * 增加 或 编辑 班级信息
    102. */
    103. export const toggleClass = data => {
    104. return new Promise((resolve, reject) => {
    105. //打开游标
    106. let {store, index} = openTransactionIndex(storeName, 'name', CONST_READ.READWRITE);
    107. let res;
    108. //ID存在,则为编辑
    109. if(data['id']&&data.id!=0){
    110. data['updatetime'] = new Date().getTime();
    111. //获取原数据
    112. res = store.get(data.id);
    113. res.onerror = function(e){
    114. reject(
    115. rJson(0, e, '查询出错了~')
    116. );
    117. }
    118. res.onsuccess = function(e){
    119. let result = e.target.result;
    120. if(result){
    121. //合并数据,并保存
    122. res = store.put(Object.assign(result, data));
    123. res.onerror = function(e){
    124. reject(
    125. rJson(0, e, '查询出错了~')
    126. );
    127. }
    128. res.onsuccess = function(){
    129. resolve(
    130. rJson(1, null, '保存成功~')
    131. )
    132. }
    133. }else{
    134. reject(
    135. rJson(0, e, '年级不存在~')
    136. );
    137. }
    138. }
    139. }
    140. //新增(需要判断是否已存在)
    141. else{
    142. //通过索引获取,判断是否已存在
    143. res = index.getKey(data.name);
    144. res.onerror = function(e){
    145. reject(
    146. rJson(0, e, '查询出错了~')
    147. );
    148. }
    149. res.onsuccess = function(e){
    150. let result = e.target.result;
    151. //如果已存在,返回错误信息
    152. if(result){
    153. reject(
    154. rJson(0, e, '该年级已存在~')
    155. );
    156. }
    157. //不存在,则直接添加
    158. else{
    159. data['createtime'] = new Date().getTime();
    160. data['updatetime'] = new Date().getTime();
    161. res = store.add(data);
    162. res.onerror = function(e){
    163. reject(
    164. rJson(0, e, '查询出错了~')
    165. );
    166. }
    167. res.onsuccess = function(){
    168. resolve(
    169. rJson(1, null, '保存成功~')
    170. )
    171. }
    172. }
    173. //if 2 end
    174. }
    175. //索引 End
    176. }
    177. //if end
    178. });
    179. }
    180. /**
    181. * 通过ID删除班级
    182. */
    183. export const deleteClassById = id => {
    184. return new Promise((resolve, reject) => {
    185. //打开游标
    186. let {store} = openTransactionIndex(storeName, undefined, CONST_READ.READWRITE);
    187. //删除指定信息
    188. let res = store.delete(id);
    189. res.onerror = function(e){
    190. reject(
    191. rJson(0, e, '操作出错了~')
    192. );
    193. }
    194. res.onsuccess = function(){
    195. resolve(
    196. rJson(1, null, '删除成功~')
    197. )
    198. }
    199. });
    200. }
    201. /**
    202. * 通过ID删除多条班级
    203. */
    204. export const deleteClassByIds = ids => {
    205. return new Promise((resolve, reject) => {
    206. if(Array.isArray(ids)){
    207. //打开游标
    208. let {store} = openTransactionIndex(storeName, undefined, CONST_READ.READWRITE);
    209. //打开游标
    210. let cursor = store.openCursor();
    211. cursor.onerror = function(e){
    212. reject(
    213. rJson(0, e, '操作出错了~')
    214. );
    215. }
    216. cursor.onsuccess = function(e){
    217. let result = e.target.result;
    218. if(result){
    219. if(ids.includes(result.key)){
    220. result.delete();
    221. }
    222. result.continue();
    223. }else{
    224. resolve(
    225. rJson(1, null, '删除成功~')
    226. )
    227. }
    228. }
    229. }else{
    230. reject(
    231. rJson(0, e, '请传入数组形式ID数据~')
    232. );
    233. }
    234. //end
    235. });
    236. }

    4.2 api接口

            api/index.js中添加班级接口,代码如下:

    1. import {
    2. loadClassAllList,
    3. toggleClass,
    4. getClassInfoById,
    5. deleteClassById,
    6. deleteClassByIds,
    7. getClassInfoByKey
    8. } from '@/db/model/classify'
    9. /**
    10. * 获取班级列表
    11. */
    12. export const getClassList = name => {
    13. return loadClassAllList(name);
    14. }
    15. /**
    16. * 新增或保存 班级信息
    17. */
    18. export const toggleClassInfo = param => {
    19. return toggleClass(param);
    20. }
    21. /**
    22. * 通过索引获取对应班级信息
    23. */
    24. export const getClassByKey = key => {
    25. return getClassInfoByKey(key);
    26. }
    27. /**
    28. * 获取班级信息 通过ID
    29. */
    30. export const getClassifyById = id => {
    31. return getClassInfoById(id);
    32. }
    33. /**
    34. * 通过ID删除指定班级信息
    35. */
    36. export const deleteClassInfoById = id => {
    37. return deleteClassById(id);
    38. }
    39. /**
    40. * 通过选择中的ID删除 班级信息
    41. */
    42. export const deleteClassSelectedInfoByIds = ids => {
    43. return deleteClassByIds(ids);
    44. }

    4.3 新增班级页面

            在components目录中添加ClassDialog/index.vue新增页面组件,html部分代码:

            这里与其他新增页面不同之处在于加载时,同时需要获取年级和教师数据列表,用于下拉选项,这两个接口已在api/index.js定义过,直接调用即可。

            这里也作了些偷懒操作,每个班级所对应课程并未作动态添加功能,而是写死在新增页面。保存的时候是以数组形式存库的,所以在编辑的时候,通过班级ID获取到班级信息,需将拿到ts字段对应信息,重新赋值给courseList。

            js代码部分:

    新增界面如下:

            

    4.4 班级列表页

            在pages/classify/index.vue中添加列表页面,html代码如下:

    js部分代码:

    列表界面如下:

    4.5 查询关联数据 

            如上图所示,现在班主任和年级信息无法显示,因为这里我们保存的是教师ID和年级ID,所以现在需要在列表查询功能中,增加master和grade两个字段,将查询到对应的数据赋值即可。

            并且想要拿到对应id的name值,则需要去对应teacher表和grade表中进行查询,所以还要在对应表的操作文件中添加相应查询功能函数。通过传入的ID集进行匹配相应数据,并以ID为键名保存到新的变量值中,以便与教师列表中的ID相关联。

    第一步:打开db/model/teacher.js,添加getTeacherByIdsCursor()函数,代码如下:

    1. /**
    2. * 通过游标获取对应数据集
    3. */
    4. export const getTeacherByIdsCursor = ids => {
    5. return new Promise((resolve, reject) => {
    6. //打开游标
    7. let {store} = openTransactionIndex(storeName);
    8. //通过ID获取数据
    9. let cursor = store.openCursor(),
    10. reData = {};
    11. cursor.onerror = function(e){
    12. reject();
    13. }
    14. cursor.onsuccess = function(e){
    15. let result = e.target.result;
    16. if(result){
    17. if(ids.includes(result.value.id)){
    18. reData[result.value.id] = result.value;
    19. }
    20. result.continue();
    21. }else{
    22. resolve(reData);
    23. }
    24. }
    25. //end
    26. });
    27. }

    第二步:打开db/model/grade.js,添加getGradeByIdsCursor()函数,代码如下:

    1. /**
    2. * 通过游标获取对应数据集
    3. */
    4. export const getGradeByIdsCursor = ids => {
    5. return new Promise((resolve, reject) => {
    6. //打开游标
    7. let {store} = openTransactionIndex(storeName);
    8. //通过ID获取数据
    9. let cursor = store.openCursor(),
    10. reData = {};
    11. cursor.onerror = function(e){
    12. reject();
    13. }
    14. cursor.onsuccess = function(e){
    15. let result = e.target.result;
    16. if(result){
    17. if(ids.includes(result.value.id)){
    18. reData[result.value.id] = result.value;
    19. }
    20. result.continue();
    21. }else{
    22. resolve(reData);
    23. }
    24. }
    25. //end
    26. });
    27. }

    第三步:打开db/model/classify.js文件,修改loadClassAllList()函数,代码如下:

    1. /**
    2. * 获取 班级信息
    3. */
    4. export const loadClassAllList = async (name) => {
    5. return new Promise((resolve, reject) => {
    6. //打开游标
    7. let {store} = openTransactionIndex(storeName);
    8. //获取所有数据
    9. let alls = store.getAll();
    10. alls.onerror = function(e){
    11. reject(
    12. rJson(0, e, '查询出错了~')
    13. );
    14. }
    15. alls.onsuccess = function(e){
    16. let result = e.target.result;
    17. if(result){
    18. let rData = result;
    19. if(name){
    20. rData = result.filter(item => item.name.includes(name));
    21. }
    22. let teacherIds = [];
    23. //拿到不重复的教师ID
    24. rData.forEach(item => {
    25. if(!teacherIds.includes(item.mid)){
    26. teacherIds.push(item.mid);
    27. }
    28. if(Array.isArray(item['ts'])&&item.ts.length>0){
    29. item.ts.forEach(sub => {
    30. if(!teacherIds.includes(sub.tid)){
    31. teacherIds.push(sub.tid);
    32. }
    33. });
    34. }
    35. })
    36. //查询关联数据
    37. Promise.all([
    38. getTeacherByIdsCursor(teacherIds),
    39. getGradeByIdsCursor(rData.map(item => item.gid))
    40. ]).then(res => {
    41. let teacher = res[0],
    42. grade = res[1];
    43. //重组数据
    44. rData = rData.map(item => {
    45. if('undefined'!==typeof teacher[item.mid]){
    46. item['master'] = teacher[item.mid]['name'];
    47. }
    48. if('undefined'!==typeof grade[item.gid]){
    49. item['grade'] = grade[item.gid]['name'];
    50. }
    51. //查询到授课教师信息
    52. if(Array.isArray(item['ts'])&&item.ts.length>0){
    53. item.ts = item.ts.map(sub => {
    54. if('undefined'!==typeof teacher[sub.tid]){
    55. sub['teacher'] = teacher[sub.tid]['name'];
    56. }
    57. return sub;
    58. })
    59. }
    60. return item;
    61. });
    62. resolve(
    63. rJson(1, rData, '获取成功~')
    64. )
    65. }).catch(e => {
    66. reject(
    67. rJson(0, null, '关联数据查询错误~')
    68. )
    69. });
    70. }else{
    71. reject(
    72. rJson(0, null, '未查询到数据~')
    73. )
    74. }
    75. }
    76. });
    77. }

    此时再查看列表页,班主任和年级信息则显示出来了,如下图:

    五、学员列表

    5.1 数据库操作文件

            db/model/student.js代码如下:

    1. import { DBIsLoadSuccess, openTransactionIndex, CONST_READ } from '@/db'
    2. import { rJson } from '@/utils/utils'
    3. import { getClassByIdsCursor } from './classify.js'
    4. let storeName = 'student';
    5. /**
    6. * 获取 学员列表
    7. */
    8. export const loadStudentAllList = name => {
    9. return new Promise((resolve, reject) => {
    10. //打开游标
    11. let {store} = openTransactionIndex(storeName);
    12. //获取所有数据
    13. let alls = store.getAll();
    14. alls.onerror = function(e){
    15. reject(
    16. rJson(0, e, '查询出错了~')
    17. );
    18. }
    19. alls.onsuccess = function(e){
    20. let result = e.target.result;
    21. if(result){
    22. let rData = result;
    23. if(name){
    24. rData = result.filter(item => item.name.includes(name));
    25. }
    26. resolve(
    27. rJson(1, rData, '获取成功~')
    28. )
    29. }else{
    30. reject(
    31. rJson(0, null, '未查询到数据~')
    32. )
    33. }
    34. // console.log('store', result);
    35. }
    36. });
    37. }
    38. /**
    39. * 获取学员信息
    40. */
    41. export const getStudentInfoById = id => {
    42. return new Promise((resolve, reject) => {
    43. //打开游标
    44. let {store} = openTransactionIndex(storeName);
    45. //通过ID获取数据
    46. let data = store.get(id);
    47. data.onerror = function(e){
    48. reject(
    49. rJson(0, e, '查询出错了~')
    50. );
    51. }
    52. data.onsuccess = function(e){
    53. let result = e.target.result;
    54. if(result){
    55. resolve(
    56. rJson(1, result, '获取成功~')
    57. )
    58. }else{
    59. reject(
    60. rJson(0, e, '数据不存在~')
    61. );
    62. }
    63. }
    64. });
    65. }
    66. /**
    67. * 增加 或 编辑 员学信息
    68. */
    69. export const toggleStudent = data => {
    70. return new Promise((resolve, reject) => {
    71. //打开游标
    72. let {store, index} = openTransactionIndex(storeName, 'name', CONST_READ.READWRITE);
    73. let res;
    74. //ID存在,则为编辑
    75. if(data['id']&&data.id!=0){
    76. data['updatetime'] = new Date().getTime();
    77. //获取原数据
    78. res = store.get(data.id);
    79. res.onerror = function(e){
    80. reject(
    81. rJson(0, e, '查询出错了~')
    82. );
    83. }
    84. res.onsuccess = function(e){
    85. let result = e.target.result;
    86. if(result){
    87. //合并数据,并保存
    88. res = store.put(Object.assign(result, data));
    89. res.onerror = function(){
    90. reject(
    91. rJson(0, e, '查询出错了~')
    92. );
    93. }
    94. res.onsuccess = function(){
    95. resolve(
    96. rJson(1, null, '保存成功~')
    97. )
    98. }
    99. }else{
    100. reject(
    101. rJson(0, e, '年级不存在~')
    102. );
    103. }
    104. }
    105. }
    106. //新增(需要判断是否已存在)
    107. else{
    108. //通过索引获取,判断是否已存在
    109. res = index.getKey(data.name);
    110. res.onerror = function(e){
    111. reject(
    112. rJson(0, e, '查询出错了~')
    113. );
    114. }
    115. res.onsuccess = function(e){
    116. let result = e.target.result;
    117. //如果用已存在,返回错误信息
    118. if(result){
    119. reject(
    120. rJson(0, e, '该学员已存在~')
    121. );
    122. }
    123. //不存在,则直接添加
    124. else{
    125. data['createtime'] = new Date().getTime();
    126. data['updatetime'] = new Date().getTime();
    127. res = store.add(data);
    128. res.onerror = function(){
    129. reject(
    130. rJson(0, e, '查询出错了~')
    131. );
    132. }
    133. res.onsuccess = function(){
    134. resolve(
    135. rJson(1, null, '保存成功~')
    136. )
    137. }
    138. }
    139. //if 2 end
    140. }
    141. //索引 End
    142. }
    143. //if end
    144. });
    145. }
    146. /**
    147. * 通过ID删除学员信息
    148. */
    149. export const deleteStudentById = id => {
    150. return new Promise((resolve, reject) => {
    151. //打开游标
    152. let {store} = openTransactionIndex(storeName, undefined, CONST_READ.READWRITE);
    153. //删除指定信息
    154. let res = store.delete(id);
    155. res.onerror = function(e){
    156. reject(
    157. rJson(0, e, '操作出错了~')
    158. );
    159. }
    160. res.onsuccess = function(){
    161. resolve(
    162. rJson(1, null, '删除成功~')
    163. )
    164. }
    165. });
    166. }
    167. /**
    168. * 通过ID删除多条学员信息
    169. */
    170. export const deleteStudentByIds = ids => {
    171. return new Promise((resolve, reject) => {
    172. if(Array.isArray(ids)){
    173. //打开游标
    174. let {store} = openTransactionIndex(storeName, undefined, CONST_READ.READWRITE);
    175. //打开游标
    176. let cursor = store.openCursor();
    177. cursor.onerror = function(e){
    178. reject(
    179. rJson(0, e, '操作出错了~')
    180. );
    181. }
    182. cursor.onsuccess = function(e){
    183. let result = e.target.result;
    184. if(result){
    185. if(ids.includes(result.key)){
    186. result.delete();
    187. }
    188. result.continue();
    189. }else{
    190. resolve(
    191. rJson(1, null, '删除成功~')
    192. )
    193. }
    194. }
    195. }else{
    196. reject(
    197. rJson(0, e, '请传入数组形式ID数据~')
    198. );
    199. }
    200. //end
    201. });
    202. }

    5.2 api接口

            api/index.js文件中新增学员相关接口,代码如下:

    1. import {
    2. loadStudentAllList,
    3. toggleStudent,
    4. getStudentInfoById,
    5. deleteStudentById,
    6. deleteStudentByIds
    7. } from '@/db/model/student'
    8. /**
    9. * 获取学员列表数据
    10. */
    11. export const getStudentList = name => {
    12. return loadStudentAllList(name);
    13. }
    14. /**
    15. * 通过ID获取学员信息
    16. */
    17. export const loadStudentById = id => {
    18. return getStudentInfoById(id);
    19. }
    20. /**
    21. * 添加 或 修改学员数据
    22. */
    23. export const toggleStudentInfo = params => {
    24. return toggleStudent(params);
    25. }
    26. /**
    27. * 通过ID删除指定学员信息
    28. */
    29. export const deleteStudentInfoById = id => {
    30. return deleteStudentById(id);
    31. }
    32. /**
    33. * 删除选中的学员信息
    34. */
    35. export const deleteStudentInfoByIds = ids => {
    36. return deleteStudentByIds(ids);
    37. }

    5.3 新增页面

            在components/StudentDialog/index.vue中添加新增页面,html代码如下:

    1. <template>
    2. <el-dialog :title="uid!=0?'编辑':'新增'" :visible="visible" @close="closeEvent" width="420px" class="dialog-wrap">
    3. <el-form :model="form" :rules="rules" status-icon ref="ruleForm">
    4. <el-form-item label="学员姓名" :label-width="formLabelWidth" required prop="name">
    5. <el-input v-model="form.name" autocomplete="off" size="small">el-input>
    6. el-form-item>
    7. <el-form-item label="户籍" :label-width="formLabelWidth" required prop="registration">
    8. <el-input v-model="form.registration" autocomplete="off" size="small">el-input>
    9. el-form-item>
    10. <el-form-item label="手机号" :label-width="formLabelWidth" required prop="phone">
    11. <el-input v-model="form.phone" autocomplete="off" size="small">el-input>
    12. el-form-item>
    13. <el-form-item label="年级" :label-width="formLabelWidth" prop="gid">
    14. <el-select v-model="form.gid" placeholder="请选择" @change="gradeChange">
    15. <el-option
    16. v-for="item in gradeOptions"
    17. :key="item.value"
    18. :label="item.label"
    19. :value="item.value">
    20. el-option>
    21. el-select>
    22. el-form-item>
    23. <el-form-item label="班级" :label-width="formLabelWidth" prop="cid">
    24. <el-select v-model="form.cid" placeholder="请选择">
    25. <el-option
    26. v-for="item in classOptions"
    27. :key="item.value"
    28. :label="item.label"
    29. :value="item.value">
    30. el-option>
    31. el-select>
    32. el-form-item>
    33. <el-form-item label="居住地址" :label-width="formLabelWidth" required prop="address">
    34. <el-input v-model="form.address" autocomplete="off" size="small">el-input>
    35. el-form-item>
    36. <el-form-item label="出生日期" :label-width="formLabelWidth" required prop="birthday">
    37. <el-date-picker v-model="form.birthday" value-format="yyyy-MM-dd" type="date" placeholder="选择日期">el-date-picker>
    38. el-form-item>
    39. el-form>
    40. <div slot="footer" class="dialog-footer">
    41. <el-button size="small" @click="closeEvent">取 消el-button>
    42. <el-button size="small" type="primary" @click="submitForm">保 存el-button>
    43. div>
    44. el-dialog>
    45. template>

    js代码如下:

    界面如下:

             这里需要注意的是,为新增功能时,先加载年级数据列表,这部分接口是在之前年级功能实现就已经定义了,直接调用即可;当年级数据变化选中即,通过年级ID查询到对应的班级列表数据即可,这块接口也是在api/index.js中定义过的。

            当为编辑功能时,获取到详情数据需判断年级ID是否存在,如果存在则需要通过年级ID查询 出对应班级数据列表。

    5.4 列表页面

            在pages/student/index.vue中添加列表页面,html代码如下:

    js代码部分:

    界面效果如下:

    5.5 关联数据查询

            这里出现同样的问题,关键班级的信息未显示出来,和班级中查询关联数据一样,以同样方式即可。

    第一步:打开db\model\classify.js文件,添加getClassByIdsCursor()函数,代码如下:

    1. /**
    2. * 通过游标获取对应数据集
    3. */
    4. export const getClassByIdsCursor = ids => {
    5. return new Promise((resolve, reject) => {
    6. //打开游标
    7. let {store} = openTransactionIndex(storeName);
    8. //通过ID获取数据
    9. let cursor = store.openCursor(),
    10. reData = {};
    11. cursor.onerror = function(e){
    12. reject();
    13. }
    14. cursor.onsuccess = function(e){
    15. let result = e.target.result;
    16. if(result){
    17. if(ids.includes(result.value.id)){
    18. reData[result.value.id] = result.value;
    19. }
    20. result.continue();
    21. }else{
    22. resolve(reData);
    23. }
    24. }
    25. //end
    26. });
    27. }

     第二步:打开db\model\student.js文件,修改loadStudentAllList()函数,代码如下:

    1. /**
    2. * 获取 学员列表
    3. */
    4. export const loadStudentAllList = name => {
    5. return new Promise((resolve, reject) => {
    6. //打开游标
    7. let {store} = openTransactionIndex(storeName);
    8. //获取所有数据
    9. let alls = store.getAll();
    10. alls.onerror = function(e){
    11. reject(
    12. rJson(0, e, '查询出错了~')
    13. );
    14. }
    15. alls.onsuccess = function(e){
    16. let result = e.target.result;
    17. if(result){
    18. let rData = result;
    19. if(name){
    20. rData = result.filter(item => item.name.includes(name));
    21. }
    22. getClassByIdsCursor(rData.filter(item => item.cid).map(item => item.cid)).then(res => {
    23. rData = rData.map(item => {
    24. if('undefined'!==typeof res[item.cid]){
    25. item['classify'] = res[item.cid]['name'];
    26. }
    27. return item;
    28. })
    29. resolve(
    30. rJson(1, rData, '获取成功~')
    31. )
    32. }).catch(e => {
    33. reject(
    34. rJson(0, e, '查询出错了~')
    35. )
    36. });
    37. }else{
    38. reject(
    39. rJson(0, null, '未查询到数据~')
    40. )
    41. }
    42. }
    43. });
    44. }

    现在列表页面效果如下:

    六、分页功能

            这里我们以学员列表为例,添加分页查询功能。注意的是,其他页面列表查询已在各关键页面中使用,为了避免影响别的功能,建议新建分页查询 函数。

    6.1 分页功能函数

            首先打开src/utils/utils.js文件,添加分页功能函数,代码如下:

    1. /**
    2. * 生成分页数据
    3. */
    4. export const genderPage = (data, param) => {
    5. //判断分页数据是否存在,否则赋值默认参数
    6. param = param && 'undefined'!==typeof param['page'] && 'undefined'!==typeof param['pageSize'] ? param : {
    7. page: 1,
    8. pageSize: 10
    9. }
    10. let newData = data.map(item => item),
    11. start = (param.page - 1) * param.pageSize,
    12. end = newData.length - start < param.pageSize ? newData.length : start + param.pageSize;
    13. return newData.slice(start, end);
    14. }

    6.2 增加分页查询功能

            打开db/model/student.js,添加分页查询函数loadStudentPage(),代码如下:

    1. import { DBIsLoadSuccess, openTransactionIndex, CONST_READ } from '@/db'
    2. import { rJson, genderPage } from '@/utils/utils'
    3. import { getClassByIdsCursor } from './classify.js'
    4. let storeName = 'student';
    5. /**
    6. * 获取 学员列表 - 分页模式
    7. * @param name 查询关键词
    8. * @param param 分页参数
    9. */
    10. export const loadStudentPage = (name, param) => {
    11. return new Promise((resolve, reject) => {
    12. //打开游标
    13. let {store} = openTransactionIndex(storeName);
    14. //获取所有数据
    15. let alls = store.getAll();
    16. alls.onerror = function(e){
    17. reject(
    18. rJson(0, e, '查询出错了~')
    19. );
    20. }
    21. alls.onsuccess = function(e){
    22. let result = e.target.result;
    23. if(result){
    24. let rData = result;
    25. if(name){
    26. rData = result.filter(item => item.name.includes(name));
    27. }
    28. //查询班级关联数据
    29. getClassByIdsCursor(rData.filter(item => item.cid).map(item => item.cid)).then(res => {
    30. rData = rData.map(item => {
    31. if('undefined'!==typeof res[item.cid]){
    32. item['classify'] = res[item.cid]['name'];
    33. }
    34. return item;
    35. })
    36. //通过genderPageData函数进行分页处理
    37. resolve(
    38. rJson(1, {
    39. list: genderPage(rData, param),
    40. total: rData.length
    41. }, '获取成功~')
    42. )
    43. }).catch(e => {
    44. reject(
    45. rJson(0, e, '查询出错了~')
    46. )
    47. });
    48. }else{
    49. reject(
    50. rJson(0, null, '未查询到数据~')
    51. )
    52. }
    53. }
    54. });
    55. }

    6.3 api接口

            打开api/index.js,添加分页查询接口函数,代码如下:

    1. import {
    2. loadStudentPage
    3. } from '@/db/model/student'
    4. /**
    5. * 获取学员列表数据 - 分页模式
    6. */
    7. export const getStudentPageList = (name, param) => {
    8. return loadStudentPage(name, param);
    9. }

    6.4 添加分页代码

    html部分,在列表下添加分页代码,代码如下:

    1. <div class="table-wrap">
    2. ...
    3. <el-pagination
    4. background
    5. layout="prev, pager, next"
    6. @current-change="currentChange"
    7. :current-page="page"
    8. :page-size="pageSize"
    9. :total="pageTotal">
    10. el-pagination>
    11. div>

    js部分,引入getStudentPageList接口函数,在data中添加分页参数,增加分页切换事件,代码如下:

    1. import StudentDialog from '@/components/StudentDialog'
    2. import { getStudentList, getStudentPageList, deleteStudentInfoById, deleteStudentInfoByIds } from '@/api'
    3. import { formatDate } from '@/utils/utils'
    4. export default {
    5. data () {
    6. return {
    7. //...
    8. //分页参数
    9. page: 1,
    10. pageSize: 5,
    11. pageTotal: 0
    12. }
    13. },
    14. filters: {
    15. filterAge(val){
    16. let current = new Date(),
    17. bDate = new Date(val);
    18. return current.getFullYear() - bDate.getFullYear();
    19. }
    20. },
    21. components: {
    22. StudentDialog
    23. },
    24. created() {
    25. this.updateList();
    26. },
    27. methods: {
    28. /**
    29. * 当前页发生改变
    30. */
    31. currentChange(page){
    32. this.page = page;
    33. this.updateList();
    34. }
    35. }

    6.5 修改列表查询函数

            由于分页查询功能中需要返回数据总量,则原来返回结果结构发生变化,所以updateList()函数需要稍微调整下,代码如下:

    1. methods: {
    2. /**
    3. * 获取列表数据
    4. */
    5. updateList(){
    6. getStudentPageList(this.keyword, {
    7. page: this.page,
    8. pageSize: this.pageSize
    9. }).then(res => {
    10. if(res.code==1){
    11. this.pageTotal = res.data['total'];
    12. this.tableList = res.data['list'].map(item => {
    13. item['createtime'] = formatDate(item.createtime);
    14. item['updatetime'] = formatDate(item.updatetime);
    15. return item;
    16. });
    17. }
    18. }).catch(e => {
    19. console.error(e);
    20. })
    21. },
    22. //...
    23. }

    此时页面效果如下:

            到此为止,该系统开发则已经完结了,有兴趣的朋友可以再升级和优化下。

            由于近期工作原因,不是每天都能抽出时间开发,所以有些不太连贯,望见谅。此篇对增删改查也没作太多细讲,因为都差不多,大家可以拷贝代码到本地运行自行研究。

  • 相关阅读:
    一起Talk Android吧(第四百一十回:绘制曲线)
    Python中的AI库有哪些?
    鸿鹄工程项目管理系统 Spring Cloud+Spring Boot+Mybatis+Vue+ElementUI+前后端分离构建工程项目管理系统项目背景
    AndroidT(13) -- C 语言格式的LOG输出(一)
    快鲸SCRM能为企业解决哪些管理痛点?
    日本人之间交谈的一个方式了解下
    YOLOv8改进 | 2023 | SCConv空间和通道重构卷积(精细化检测,又轻量又提点)
    基于Django+MySQL的智慧校园系统
    Servlet生命周期知识点汇总
    深入理解JVM虚拟机_1 JVM类加载器设计原理
  • 原文地址:https://blog.csdn.net/jiciqiang/article/details/127463512