vue后台管理系统项目

摘要:
项目简介1.项目根目录文件2.源代码子目录结构3.api目录4.资产目录5.组件目录6.mixins目录7.权限目录8.路由器目录9.存储目录10.样式目录11.utils目录项目简介1.安装元素ui组件以按需加载//1.1.npmielement-ui-S//1.2.配置模块。e在babel.config.js中
项目介绍

1.项目根目录文件

vue后台管理系统项目第1张

2.源码子目录结构

vue后台管理系统项目第2张

3.api目录

vue后台管理系统项目第3张

4.assets目录

vue后台管理系统项目第4张

5.components目录

vue后台管理系统项目第5张

6.mixins目录

vue后台管理系统项目第6张

7.permission目录

vue后台管理系统项目第7张

8.router目录

vue后台管理系统项目第8张

9.store目录

vue后台管理系统项目第9张

10.styles目录

vue后台管理系统项目第10张

11.utils目录

vue后台管理系统项目第11张

项目文件介绍

1.安装element-ui组件实现按需加载

// 1.1.npm i element-ui -S

// 1.2.在babel.config.js中配置

module.exports = {

    plugins: [

        [
              'component',
              {
                libraryName: 'element-ui',
                styleLibraryName: 'theme-chalk'
              }
        ],
    ]
     
}

// utils下新建文件Element.js按需加载组件

import Vue from 'vue'

// 按需引入

import {

  Button,

} from 'element-ui'

// 注册组件,之后就可以在所有组件中使用

Vue.use(Button)

/*
    注意:列如消息类组件,如:Message,Notification,MessageBox等
    
    不能像上面一样注册使用,否则还没有使用该组件该组件就会被初始化
*/


// 解决:绑定到vue原型上

Vue.prototype.$message = Message
Vue.prototype.$notify = Notification
Vue.prototype.$confirm = MessageBox

// 再组件使用时直接this调用即可

this.$message.success('注册成功')

// 如果想使用MessageBox中的确认弹框

this.$confirm.confirm()

2.封装二次请求使用起来更方便,在utils中新建request.js中写入

// 安装第三方请求工具axios

npm install axios

// request.js

import axios from 'axios'

// 处理javascript大数字的问题

// 安装 npm i json-bigint

import jsonBig from 'json-bigint'


import store from '@/store/index'


// element-ui的加载组件和消息提示组件
import { message, Loading } from 'element-ui'

// 是我们项目的配置文件,项目的接口和其他的配置放在utils目录中的config.js中

import { config, loaddingConfig } from '@/utils/config'

// 创建一个新的axios对象供我们使用
const request = axios.create({

  baseURL: `${config.baseURL}${config.prot}`,
  timeout: config.timeout
})


// 请求拦截器

// 注意在单独的js中使用Loading和在组件中使用有一点不同

/*

    1.在组件中使用

    const loading = this.$loading({
          lock: true,
          text: 'Loading',
          spinner: 'el-icon-loading',
          background: 'rgba(0, 0, 0, 0.7)'
     });


    关闭加载

    loading.close()


    2.在单独的js文件中使用

    Loading.service({

         lock: true,
         text: 'Loading',
         spinner: 'el-icon-loading',
         background: 'rgba(0, 0, 0, 0.7)'
    })

    关闭加载状态
    
    Loading.service().close()

*/ 

request.interceptors.request.use(config => {
  
  // 开始加载
  Loading.service(loaddingConfig)

  if (store.getters.getuserInfo.token) {

    // 每次登陆的时候将token插入到请求头中
    config.headers.authorization = store.getters.getuserInfo.token
  }

  return config

}, error => {

  // 取消失败终止
  Loading.service().close()
  return Promise.reject(error)
})



// 处理大数字的问题
request.defaults.transformResponse = [function (data) {
  try {
    return jsonBig.parse(data)
  } catch (err) {
    return data
  }
}]



// 响应拦截器
request.interceptors.response.use(response => {

  // 请求完毕取消加载
  Loading.service().close()
  return response
}, error => {

  Loading.service().close()

    // 处理请求错误的函数
  errorMsg(error.response)

  return Promise.reject(error)
})


// 异常处理
function errorMsg (error) {

  if (error) {

    return
  }

  switch (error.status) {
    case 400:
      message.error('亲,您查看的资料出现了错误')
      break
    case 401:
      message.error('请检查token,可能已经过期,需要重新登陆')
      tokenOverdue(error)
      break
    case 403:
      message.error('抱歉您的权限还需要升级')
      break
    case 404:
      message.error('资源被狗狗调走了')
      break
    case 408:
      message.error('请求超时,请重试')
      break
    case 500:
      message.error('可爱的服务器好像奔溃了')
      break
    case 502:
      message.error('请仔细检查您的网络')
      break
    case 504:
      message.error('您的网络很慢,已经超时,请重试')
      break
    case 503:
      message.error('当前服务不支持')
      break
    default:
      message.error('与服务器的连接断开了')
      break
  }
}


// token的过期处理

function tokenOverdue (error) {
  // 如果有token的话重新存储,刷新

  if (error.data.token) {
    // 将刷新的token重新存储到本地即可
    const userInfo = {
      ...store.getters.getuserInfo,
      token: error.data.token
    }
    
    // 将新的token重新存储到本地,
    store.commit('SET_USERINFO', userInfo)
    
    // 将错误的请求再重新发送刷新token
    return request(error.config)
  }
}


// 导出请求接口封装函数
export default (method, url, data = null) => {
  method = method.toUpperCase()

  if (method === 'POST') {
    return request.post(url, data)
  } else if (method === 'GET') {
    return request.get(url, { params: data })
  } else if (method === 'DELETE') {
    return request.delete(url, { params: data })
  } else if (method === 'PUT') {
    return request.put(url, data)
  }
}

3.创建api根目录,里面当接口文件

// user.js文件

import request from '@/utils/request'

// 用户登陆 export const userLogin = params => request('GET', '/admin/user/login', params) // 用户注册 export const userRegister = params => request('GET', '/admin/user/register', params) // 获取用户的权限数据 export const getUserAuth = params => request('GET', '/admin/user/user_auth', params) // 获取用户头像 export const getUserImg = params => request('GET', '/admin/user/user_img', params) // 获取用户登陆时的头像 export const getLoginImg = params => request('GET', '/admin/user/login_img', params) // 修改用户资料 export const editUserInfo = data => request('POST', '/admin/user/edit_user', data)

4.require.context(path, Boolean, file)动态加载组件

  1.为什么使用下一步components目录中的组件要引入使用,那么就存在一个问题,如果我们一个页面要引入很多组件那么就会有很多的import。

     此时可以通过使用require.context(path, Boolean, file)之后就可以通过组件的name属性来调用组件

// 在components目录下新建 cptsRegister.js

// cptsRegister.js

import Vue from 'vue'

function capitalizeFirstLetter (str) {
  return str.charAt(0).toUpperCase() + str.slice(1)
}

// 用来匹配.vue的前缀函数
function validateFileName (str) {
  return /^S+.vue$/.test(str) && str.replace(/^S+/(w+).vue$/, (res, $1) => capitalizeFirstLetter($1))
}

const requireComponent = require.context('./', true, /.vue$/)

// 遍历匹配到的文件夹及文件名,并且遍历得到每一个
requireComponent.keys().forEach(filePath => {

  // 得到每一个.vue文件中的属性方法和组件的name值
  const componentConfig = requireComponent(filePath)
  // 得到文件名的前半部分index
  const fileName = validateFileName(filePath)
  // 判断如果是以index开头命名的就返回组件的name值来注册组件,否则就使用文件名来注册

  const componentName = fileName.toLowerCase() === 'index' ? 
capitalizeFirstLetter(componentConfig.default.name) : fileName
  Vue.component(componentName, componentConfig.default || componentConfig)
})


// components目录下的cheking目录的vue

<template>

    <div class="container">
        我是一个组件
    </div>

</template>

<script>

export default {

    name: 'Checking',
    
}

</script>


// 其他组件中使用,直接使用,无需引入和注册,但是必须和组件的name名字保持一致,是不是很棒

<Checking />

5.处理一下过滤器把

// 在utils目录下创建filters.js文件,我们一次性注册掉所有的过滤器

// filter.js

// 事件格式化第三方插件
import moment from 'moment'

const filters = {

  relativeTime (value) {
    return moment(value).startOf('second').fromNow()
  },

  formatTime (value, format = 'YYYY-MM-DD HH:mm:ss') {
    return moment(value).format(format)
  },

  statusFilter (status) {
    const statusText = ['审核通过', '草稿', '待审核', '审核失败']

    if (status === undefined) return []

    if (status.length) {

      status.forEach(item => {
        
        item.statusContent = statusText[item.status]
      })
    }
    return status
  }
}

export default filters


// 之后在main.js中一次性注册


import filterRegister from './utils/filters'

for (const key in filterRegister) {

  Vue.filter(key, filterRegister[key])
}

6.图片懒加载( vue-lazyload )

// 安装 vue-lazyload 插件

npm i vue-lazyload

// 在main.js中引入使用

import VueLazyload from 'vue-lazyload'

// 引入图片懒加载的配置文件
import { lazyLoad } from './utils/config'


// 注册使用
Vue.use(VueLazyload, lazyLoad)

// 在组件中img标签上的src属性替换成v-lazy="reqquire('../../assets/images')",
注意图片的地址必须是网络地址,如果是本地地址需要跳过require()导入使用


// config中的配置

// 图片懒加载的配置
export const lazyLoad = {

  preLoad: 1.3,

  // 加载中显示的图片
  loading: 'https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=2843662159,2317606805&fm=16&gp=0.jpg',

  // 加载失败显示的图片
  error: 'https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=104394466,2625116464&fm=11&gp=0.jpg',

  // 尝试加载一次
  attempt: 1
}

7.路由切换进度条(nprogress

// 安装进度条插件

npm i nprogress

// 在全局路由导航中使用,在目录permission下的index.js中使用

import router from '@/router'

// 导入进度条
import nprogress from 'nprogress'

router.beforeEach((to, from, next) => {

   // 开启进度条
  nprogress.start()
})


router.afterEach((to, from, next) => {
  // 关闭进度条
  nprogress.done()
})


// main.js中引入样式文件

// 引入滚动条的样式文件
import 'nprogress/nprogress.css'


// 引入全局路由导航拦截
import './permission/index'


// 自定义滚动条的颜色

#nprogress .bar {
  background: #c3f909 !important;
}

8.看一下路由的设置

import Vue from 'vue'
import VueRouter from 'vue-router'

// 公共路由
export const publicRoutesMap = [
  {
    path: '/login',
    name: 'login',
    component: () => import(/* webpackChunkName: "user" */ '@/views/Login')
  },
  {
    path: '/register',
    name: 'register',
    component: () => import(/* webpackChunkName: "user" */ '@/views/Register')
  },
]


// 权限路由

// 需要的权限路由,动态挂载的路由
export const asyncRoutesMap = [
  {
    path: '*',
    redirect: '/404',
    hidden: true,
    meta: {
      roles: ['user']
    }
  },
  {
    path: '/404',
    name: 'four',
    component: () => import('../views/Error/foruAndFour.vue'),
    meta: {
      roles: ['user']
    }
  }
]

9.在store目录下的modules目录新建permission.js文件

// 把公共路由和权限的路由导出来
import router, { publicRoutesMap, asyncRoutesMap } from '@/router'

// 定义一个函数用来筛选后端返回来的权限数据,如果筛选成功的话返回true,否则false
function hasPerMission (roles, route) {
  if (route && route.meta.roles) {
    return roles.some(role => route.meta.roles.indexOf(role) >= 0)
  } else {
    return true
  }
}

const permission = {

  state: {
    routers: publicRoutesMap,
    addRouters: []
  },
  mutations: {
    SET_ROUTERS (state, routers) {
      state.addRouters = routers
      state.routers = publicRoutesMap.concat(routers)
    }
  },
  actions: {
    generateRoutes ({ state, commit }, data) {
      // 返回一个promise回调

      return new Promise((resolve, reject) => {
        // 遍历权限数组
        const accessedRoutes = asyncRoutesMap.filter(v => {
          // 如果包含admin,说明就是管理员直接进入即可
          if (data.indexOf('admin') >= 0) return true

          // 之后就是调用hasPerMission函数对象权限动态路由和后台返回的用户权限进行严格匹配
          if (hasPerMission(data, v)) {
            // 判断是否有权限路由是否有子路由,有子路由继续遍历
            if (v.children && v.children.length > 0) {
              v.children = v.children.filter(child => {
                // 对权限子路由和后台返回的用户权限数据,在进行匹配,匹配成功返回
                if (hasPerMission(data, child)) {
                  return child
                }
                // 失败返回false
                return false
              })

              // 并且要把权限的父路由返回来,不光要把权限子路由返回,
              // 无论权限子路有还是没有,都应该把权限父路由返回来
              return v
            } else {
              // 否则说明没有子路由,直接把父路由返回
              return v
            }
          }

          // 如果权限验证没有匹配项,直接返回false
          return false
        })

        // 将返回的动态路由存储到公共路由中
        commit('SET_ROUTERS', accessedRoutes)
        resolve()
      })
    }
  },
  getters: {
    // 只要权限路由数组发生变化就重新计算
    addRouters (state) {
      return state.routers
    }
  }
}

export default permission

10.在全局路由导航中进行拦截当天添加

import router from '@/router'
import store from '@/store'

// 导入进度条
import nprogress from 'nprogress'

const whiteList = ['/login', '/register'] // 不重定向白名单

router.beforeEach((to, from, next) => {
  // 开启进度条
  nprogress.start()

  // 检查token
  if (store.getters.getuserInfo.token) {
    // 有token的话去登录页,直接拦截到首页,因为我们需要用户点击按钮退出,不希望通过浏览器的back按钮
    if (to.path === '/login') {
      next()
    } else {
      // 如果没有用户的权限数据,去拉取一下用户的权限数据
      if (store.getters.roles.length === 0) {

        store.dispatch('getUserAuth', store.getters.getuserInfo.token).then(res => {
          
          // 调用该方法对用户的权限进行一次筛选
          store.dispatch('generateRoutes', res).then(() => {

            router.addRoutes(store.getters.addRouters)

            if (from.path !== '/login') {
              next({ ...to, replace: true })
            } else {
              next()
            }
          })
        }).catch(error => {
          // 验证失败重新登陆
          next({ path: '/login' })
        })
      } else {
        next()
      }
    }
  } else {

    // 需要重定向的白名单
    if (whiteList.indexOf(to.path) !== -1) {
      next()
    } else {
      next('/login')
    }
  }
})

router.afterEach((to, from, next) => {
  // 关闭进度条
  nprogress.done()
})

11.看一下mixins目录中的文件

// 写法和vue一模一样,在需要使用的vue组件中混入使用即可

// mixins目录下的index.js

export const publicLogic = {

    data () {

        return {
            
            msg: "我是mixins的数据"
        }
    },

    methods: {},

    created () {},

    watch : {

    },

    mounted () {}
}


// 在其他组件中混入

<template>

</template>

<script>

import { publicLogic } from '@/mixins/index'

export default {
    
    // 通过mixins书信混入
    mixins: [publicLogic],

    data () {

        return {

        }
    },

    created () {
        
        console.log(this.msg)    // 即可拿到混入的数据
    }
}

</script>

12.pc端的滚动条插件(vue-happy-scroll)

// 安装使用

npm i vue-happy-scroll

import { HappyScroll } from 'vue-happy-scroll'

// 在组件中注册
components: {

    HappyScroll
}


<happy-scroll :min-length-v="0.2" color="rgba(3,253,244,1)" size="10" hide-horizontal>

    // ...需要滚动的y元素

</happy-scroll>


// 先取消浏览器默认的滚动

body,html {
  overflow-y: hidden;
  overflow-x: hidden;
}

13.store目录具体结构

// store下的index.js

import Vue from 'vue'
import Vuex from 'vuex'

// 把用户权限验证模块引入
import permission from './modules/permission'

// 用户存储
import user from './modules/user'

// 公共计算属性
import getters from './getters'
Vue.use(Vuex)

export default new Vuex.Store({
  modules: {
    permission,
    user
  },
  getters
})


// store目录下的getters.js

const getters = {
  getuserInfo: state => state.user.userInfo,
  roles: state => state.user.roles,
  navBars: state => state.user.navBars,
  cache: state => state.user.cache
}

export default getters


// store目录下的modules目录下的js文件,都是一个一个的模块

// user.js 用来存储放置用户的逻辑操作



// 引入api接口
import { userLogin, getUserAuth } from '@/api/user'

const user = {

  state: {

    // 需要往本地存储的用户信息包含token
    userInfo: JSON.parse(window.sessionStorage.getItem('userInfo')) || {},
    // 用户的权限信息
    roles: [],
    // 用户的导航栏数据
    navBars: JSON.parse(window.sessionStorage.getItem('nav_bar')) || {
      navBarArr: [],
      index: 0
    },
    // 缓存组件
    cache: []
  },

  mutations: {

    SET_USERINFO (state, userInfo) {
      state.userInfo = userInfo
      window.sessionStorage.setItem('userInfo', JSON.stringify(state.userInfo))
    },

    // 存储用户的navBar导航栏数据
    SET_USERNAVBAR ({ navBars }, navBar) {

      if (navBars.navBarArr.length === 0) {
  
        navBars.navBarArr.push(navBar)
      } else {
  
        const navItem = navBars.navBarArr.find((item, index) => {
          if (item.path === navBar.path) {
            navBars.index = index
            return true
          }
        })
  
        if (!navItem) {
          navBars.navBarArr.push(navBar)
          navBars.index = navBars.navBarArr.length - 1
        }
      }
  
      window.sessionStorage.setItem('nav_bar', JSON.stringify(navBars))
    },

    // 动态添加缓存组件
    ADD_CACHE ({ cache }, name) {
      if (cache.includes(name)) return

      cache.push(name)
    },

    // 动态删除缓存组件
    REMOVE_CACHE ({ cache }, name) {
      const index = cache.indexOf(name)

      if (index === -1) return

      cache.splice(index, 1)
    }
  },

  actions: {

    // 获取用户信息
    userLogin ({ commit }, userInfo) {
      return new Promise((resolve, reject) => {
        userLogin(userInfo).then(res => {
         
          if (res.data.code === 200) {
            
            commit('SET_USERINFO', res.data.userInfo)
          }
          resolve(res.data)
        }).catch(error => {

          reject(error)
        })
      })
    },

     // 获取用户权限
     getUserAuth ({ state }, token) {
      return new Promise((resolve, reject) => {
        getUserAuth({ token }).then(res => {
          state.roles.push(...res.data)
          resolve(res.data)
        }).catch(error => {
          reject(error)
        })
      })
     }
  }
}

export default user


// permission.js 用来存储放置用户的权限方法

14.vue.config.js配置文件

const CompressionPlugin = require('compression-webpack-plugin')

const path = require('path')

const BundleAnalyzer = require('webpack-bundle-analyzer').BundleAnalyzerPlugin


module.exports = {

  // 是否触发eslint检查
  lintOnSave: false,

  // 是否使用包含运行时编译器的 Vue 构建版本, 但是这会让你的应用额外增加 10kb 左右
  runtimeCompiler: false,

  publicPath: '/',

  // 打包文件的出口
  outputDir: 'dist',

  // 放置生成的css和js和img和fonts的目录
  assetsDir: 'static',

  // 数组存放html的路径
  indexPath: 'index.html',

  productionSourceMap: false,

  /*
  
    默认情况下,生成的静态资源在它们的文件名中包含了 hash 以便更好的控制缓存,

    前提是保证index.html是vue cli自动生成的,如果无法保证的话就设置为false
  */
  filenameHashing: true,

  chainWebpack: config => {
    // ============压缩图片 start============
    config.module.rule('images')
        .test(/.(png|jpe?g|gif|svg)(?.*)?$/)
        .use('url-loader')
        .loader('url-loader')
        .options({
          limit: 10240,
          outputPath: 'static/images'
        })
        .end()
    // ============压缩图片 end============
  },

  configureWebpack: config => {

    return {

      plugins: [

        // 压缩js和css和html
        new CompressionPlugin({
          test: /.js$|.html$|.css/,
          threshold: 10240,
          deleteOriginalAssets: false
        }),

        // 图形化展示打包后的详情
        new BundleAnalyzer()
      ],

      performance: {
        // 关闭webpack的性能提示
        hints:'warning',
        //入口起点的最大体积
            maxEntrypointSize: 50000000,
            //生成文件的最大体积
        maxAssetSize: 30000000,
        //只给出 js 文件的性能提示
        assetFilter: function(assetFilename) {
                return assetFilename.endsWith('.js');
            }
      },

      // 指定不打包的第三方包,那么这些包需要在html页面通过cdn引入
      externals: {
        'vue': 'Vue',
        'vuex': 'Vuex',
        'vue-router': 'VueRouter',
        'axios': 'axios',
        "moment": "moment",
        'echarts': 'echarts'
      }
    }
  },

}


// index.html

<script
   src="https://cdn.bootcss.com/vue/2.6.11/vue.runtime.min.js"
   crossorigin="anonymous"
>
</script>
<script
   src="https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js"
   crossorigin="anonymous"
>
</script>
<script
   src="https://cdn.bootcss.com/vuex/3.1.2/vuex.min.js"
   crossorigin="anonymous"
>
</script>
<script
   src="https://cdn.bootcss.com/axios/0.19.2/axios.min.js"
   crossorigin="anonymous"
>
</script>
<script
   src="https://cdn.bootcss.com/moment.js/2.24.0/moment.min.js"
   crossorigin="anonymous"
>
</script>
<script
   src="https://cdn.bootcss.com/echarts/3.7.1/echarts.min.js"
   crossorigin="anonymous"
>
</script>

15.babel.config.js

module.exports = {
  presets: [
    '@vue/cli-plugin-babel/preset'
  ],
  plugins: [
    [
      'component',
      {
        libraryName: 'element-ui',
        styleLibraryName: 'theme-chalk'
      }
    ],

    /*
    
      把es6转换成es5的时候,babel会需要一些辅助函数,如果多个源码文件都依赖这些辅助函数

      那么这些辅助函数会出现很多次,造成代码的沉余,为了不让这些辅助函数的代码重复注销

      是 babel-plugin-transform-runtime插件可以做到让他们只出现一次,将这些辅助函数帮到

      一个单独的模块 babel-runtime 中,这样做能减小项目文件的大小。
    
    */
    "@babel/plugin-transform-runtime"
  ]
}

免责声明:文章转载自《vue后台管理系统项目》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇DB2解决死锁Python + winpcap抓包和发包下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

上传代码到Gitee忽略部分文件或目录

前言:每次提交代码到Gitee都是整个项目代码提交上去,项目目录里面包括一些文件或者目录是不需要提交的,具体实现如下 一、步骤如下 1.在项目的第一层创建.giignore文件 2.在.giignore文件填写自己要屏蔽的文件和文件夹的语法内容 3.上传代码,不会上传.giignore文件里涉及的文件或者文件夹 二、.giignore里面填写语法规范...

ELK应用之Filebeat

Filebeat是本地文件的日志数据采集器,可监控日志目录或特定日志文件(tail file),并将它们转发给Elasticsearch或Logstatsh进行索引、kafka等。带有内部模块(auditd,Apache,Nginx,System和MySQL),可通过一个指定命令来简化通用日志格式的收集,解析和可视化。 官方网址:https://www...

【转】Maven pom.xml 配置详解

  原文链接:https://yq.aliyun.com/articles/38271   pom.xml文件配置详解 --声明规范 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:s...

Liunx 的常用命令

配置文件常用操作 d100回车删除 100 行gg=G 进行配置文件格式整理配置虚拟机 ipvim /etc/sysconfig/network-scripts/ifcfg-ens33修改环境变量vim /etc/profile配置完成之后source /etc/profile执行权限chmod a+x a+x 文件名 表示可执行权限,a 表示所有用户 路径...

Boost库学习(3) 内存管理

preface: 1、传统C++内存管理中可能出现的问题:内存泄露、野指针、访问越界;相关解决方案:智能指针 std::auto_ptr,不能完全解决内存管理中出现的问题; 2、关于smart_ptr库的概述: 2.1、C++程序员通常采用RAII(资源获取即初始化)机制管理内存资源,在使用资源的类的构造函数中申请资源,最后在析构函数中释放资源;如果对象的...

《QT Creator快速入门》第十五章:文件、目录、I/O

1、QIODevice QIODevice是QFile、QBuffer、QTcpSocket等I/O设备的基类,可以使用open()打开设备(打开模式如下)、read()/write()进行文件读写、close()关闭设备。 QTcpSocket、QUdpSocket、QProcess等属于顺序存储设备,它们不支持seek()来改变文件指针,QFile、Q...