记录下:自己的产品:订单模块初步完成

198 阅读5分钟

电商项目的总结

给你七年时间,你会干什么?我想不出我会干什么耶,我只有确定的一件事情就是我会全国各地跑一遍

装饰器教程

Based on vue3.0.0, vant3.0.0, vue-router v4.0.0-0, vuex^4.0.0-0, vue-cli3, mockjs, imitating Jingdong Taobao, mobile H5 e-commerce platform! 基于vue3.0.0 ,vant3.0.0,vue-router v4.0.0-0, vuex^4.0.0-0,vue-cli3,mockjs,仿京东淘宝的,移动端H5电商平台!

ionic-5 vue3 starter with pwa and tailwind setup

响应式编程入门指南 - 通俗易懂 RxJS

RxJS系列教程(四) Observable

Angular7入门辅助教程(五)——Observable(可观察对象)

RXJS教程

RxJS——给你如丝一般顺滑的编程体验(篇幅较长,建议收藏)

动画学习 rxjs

有人开源躺平

开源躺平

Angular 笔记

Angular In Depth

  1. 登录跳转问题,不需要 vuex 存储状态了,因为刷新 vuex 数据丧失了,需要使用缓存机制
  2. tabbar 页面与非tabbar页跳转互动问题
  3. 订单页面,需要提示用户去登录的,
  4. 商品列表,单个列表项的删除功能,添加,减少,清空操作,用户操作,用户体验
  5. 商品规格不应该弄一个管理,而是应该让用户在添加商品的过程自己去添加
  6. 多测试

三种价格设置

<template>
  <div class="test">
    <a-radio-group name="radioGroup" :default-value="1" v-model="selected">
      <a-radio :value="1"> 零售价 </a-radio>
      <a-radio :value="2"> 批发价 </a-radio>
      <a-radio :value="3"> 团购价 </a-radio>
    </a-radio-group>
    <a-form-model ref="ruleForm" :model="ruleForm" layout="inline">
      <div v-show="selected === 1">
        <a-form-model-item label="销售价" prop="price">
          <a-input placeholder="请输入销售价" />
        </a-form-model-item>
        <a-form-model-item label="吊牌价" prop="priceTag">
          <a-input placeholder="请输入吊牌价" />
        </a-form-model-item>
        <a-form-model-item label="是否设置为默认价格">
          <input type="radio" name="radio" :value="value" />
        </a-form-model-item>
      </div>
      <div v-show="selected === 2">
        <a-form-model-item label="销售价" prop="price">
          <a-input placeholder="请输入销售价" />
        </a-form-model-item>
        <a-form-model-item label="吊牌价" prop="priceTag">
          <a-input placeholder="请输入吊牌价" />
        </a-form-model-item>
        <a-form-model-item label="是否设置为默认价格">
          <input type="radio" name="radio" :value="value" />
        </a-form-model-item>
      </div>
      <div v-show="selected === 3">
        <a-form-model-item label="销售价" prop="price">
          <a-input placeholder="请输入销售价" />
        </a-form-model-item>
        <a-form-model-item label="吊牌价" prop="priceTag">
          <a-input placeholder="请输入吊牌价" />
        </a-form-model-item>
        <a-form-model-item label="是否设置为默认价格">
          <input type="radio" name="radio" :value="value" />
        </a-form-model-item>
      </div>
    </a-form-model>
  </div>
</template>

<script>
export default {
  name: "test",
  data() {
    return {
      ruleForm: {},
      selected: 1,
      labelCol: { span: 4 },
      wrapperCol: { span: 14 },
      value: true,
    };
  },
};
</script>
function cartesianProductOf(...args) {
  return args.reduce(
    (total, current) => {
      let ret = [];
      total.forEach(a => {
        current.forEach(b => {
          ret.push(a.concat([b]));
        });
      });
      return ret;
    },
    [
      []
    ]
  );
}
// main.js
const components = require('./components/index')
for (let componentName in components) {
  Vue.component(componentName, components[componentName])
}
// vue.config.js
const path = require("path");

function resolve(dir) {
  return path.join(__dirname, dir);
}

module.exports = {
  chainWebpack: config => {
    config.resolve.alias
      .set('components', resolve('src/components/index.js'))
  },
}
// index.js
const componentFiles = require.context('./', true, /index.js$/)
const components = componentFiles.keys().reduce((files, filePath) => {
  const fileName = filePath.replace(/^\.\/(.*)\/index\.\w+$/, '$1')
  const value = componentFiles(filePath)
  if (value.default) {
    const componentName = fileName.split('/')[0]
    files[componentName] = value.default
  } else {
    for (let key in value) {
      console.log("key===>", key, value[key])
      files[key] = value[key]
    }
  }
  return files
}, {})
module.exports = components
// entryTemplate.js
module.exports = {
  entryTemplate: (compoenntName) => {
    return `
      import ${compoenntName} from './src'
      export default ${compoenntName}
    `
  }
}
// genVueTpl.js
// index.js
const chalk = require('chalk')
const path = require('path')
const fs = require('fs')
const resolve = (...file) => path.resolve(__dirname, ...file)
const log = message => console.log(chalk.green(`${message}`))
const successLog = message => console.log(chalk.blue(`${message}`))
const errorLog = error => console.log(chalk.red(`${error}`))
// 导入模板
const {
  vueTemplate
  // entryTemplate
} = require('./template')
// 导入入口
const {
  entryTemplate
} = require('./entryTemplate')
// 生成文件
const generateFile = (path, data) => {
  if (fs.existsSync(path)) {
    errorLog(`${path}文件已存在`)
    return
  }
  return new Promise((resolve, reject) => {
    fs.writeFile(path, data, 'utf8', err => {
      if (err) {
        errorLog(err.message)
        reject(err)
      } else {
        resolve(true)
      }
    })
  })
}
log('请输入要生成的vue文件夹名称 views: xxx、comp: xxx、pageComp: xxx、 它们会生成在对应的文件目录下')
let componentName = ''
process.stdin.on('data', async chunk => {
  // 组件名称
  const inputName = String(chunk).trim().toString().split(':')[1]
  // 判断放在那个文件夹里面
  let pathName = String(chunk).trim().toString().split(':')[0]

  let componentPath = null
  let entryFile = null
  switch (pathName) {
    case 'views':
      pathName = 'views'
      componentPath = resolve(`../src/${pathName}`, inputName)
      break
    case 'comp':
      pathName = 'components'
      componentPath = resolve(`../src/${pathName}`, inputName, 'src')
      entryFile = resolve(`../src/${pathName}`, inputName, 'index.js')
      break
    case 'pageComp':
      pathName = 'pageComponents'
      componentPath = resolve(`../src/${pathName}`, inputName, 'src')
      entryFile = resolve(`../src/${pathName}`, inputName, 'index.js')
      break
  }
  // Vue页面组件路径

  // vue文件
  const vueFile = resolve(componentPath, 'index.vue')

  // 入口文件

  // 判断组件文件夹是否存在
  const hasComponentExists = fs.existsSync(componentPath)
  if (hasComponentExists) {
    errorLog(`${inputName}页面组件已存在,请重新输入`)
    return
  } else {
    log(`正在生成 ${inputName} 的目录 ${componentPath}`)
    await dotExistDirectoryCreate(componentPath)
    if (pathName === 'views') {
      log(`正在生成页面子组件 components 的目录 ${componentPath}\\components`)
      await fs.mkdir(`${componentPath}\\components`, err => {
        log(err)
      })
    }
  }
  try {
    // 获取组件名
    if (inputName.includes('/')) {
      const inputArr = inputName.split('/')
      componentName = inputArr[inputArr.length - 1]
    } else {
      componentName = inputName
    }
    log(`正在生成 vue 文件 ${vueFile}`)
    await generateFile(vueFile, vueTemplate(componentName))
    log(`正在生成 entry 文件 ${entryFile}`)
    if (entryFile) {
      await generateFile(entryFile, entryTemplate(componentName))
    }
    successLog('生成成功')
  } catch (e) {
    errorLog(e.message)
  }

  process.stdin.emit('end')
})
process.stdin.on('end', () => {
  log('exit')
  process.exit()
})

function dotExistDirectoryCreate(directory) {
  return new Promise((resolve) => {
    mkdirs(directory, function () {
      resolve(true)
    })
  })
}
// 递归创建目录
function mkdirs(directory, callback) {
  var exists = fs.existsSync(directory)
  if (exists) {
    callback()
  } else {
    mkdirs(path.dirname(directory), function () {
      fs.mkdirSync(directory)
      callback()
    })
  }
}
// template.js
module.exports = {
  vueTemplate: compoenntName => {
    return `<template>
  <div class="${compoenntName}__wrapper"></div>
</template>

<script>
export default {
  name: '${compoenntName}',

  components: {},

  mixins: [],

  props: {},

  data() {
    return {}
  },

  computed: {},

  watch: {},

  created() {},

  mounted() {},

  destroyed() {},

  methods: {}
}
</script>

<style lang="scss" scoped>
  .${compoenntName}__wrapper {

  }
</style>
`
  }
}
/*第一层if判断生产环境和开发环境*/
if (process.env.NODE_ENV === 'production') {
    /*第二层if,根据.env文件中的VUE_APP_FLAG判断是生产环境还是测试环境*/
    if (process.env.VUE_APP_FLAG === 'pro') {
        //production 生产环境
        axios.defaults.baseURL = 'http://api.xinggeyun.com';//路径

    } else {
        //test 测试环境
        axios.defaults.baseURL = 'http://192.168.0.152:8102';//路径
   }
} else { //dev 开发环境 
   axios.defaults.baseURL = 'http://192.168.0.152:8102';//路径
}
 <a-form-item label='地址' :colon="false">
  <a-cascader
    :allowClear="false"
    v-decorator="['area',{rules: [{ required: true, message: '请选择地址' }]}]"
    :options="areaList"
    placeholder="请选择地址"
    :loadData="loadAreaData"
    @change="onAreaChange"
    :getPopupContainer="(trigger) => {return trigger.parentElement}"
  ></a-cascader>
</a-form-item>
data () {
    return {
      areaList: [], // 地区数据
    }
  },
  async mounted () {
   // 获取省数据
    this.areaList = await this.getAreaList() || []
  },
methods: {
 /**
     * 获取区域
     */
    getAreaList (code) {
      return new Promise((resolve, reject) => {
        getAreaData({
          code: code ? String(code) : ''
        }).then(res => {
          console.log('获取区域------', res)

          if (res.code === '0') {
            let arr = res.data.map(item => {
              return {
                value: item.code + '',
                label: item.name,
                isLeaf: item.level === '3'
              }
            })
            return resolve(arr)
          } else {
            return resolve([])
          }
        }).catch((err) => {
          return reject(err)
        })
      })
    },
// 获取下一级数据
   async loadAreaData (selectedOptions) {
      if (!this.areaList.length) {
        this.areaList = await this.getAreaList() || []
      } else {
        const targetOption = selectedOptions[selectedOptions.length - 1]
        targetOption.loading = true
        let children = await this.getAreaList(targetOption.value) || []
        if (children.length) {
          targetOption.loading = false
          targetOption.children = children
        } else {
          targetOption.loading = false
          targetOption.isLeaf = true
        }
      }
      this.areaList = cloneDeep(this.areaList)
    },
  // 选择区后 
    onAreaChange (val, selectedOptions) {
      this.provinceCode = selectedOptions[0] ? selectedOptions[0].value : ''
      this.province = selectedOptions[0] ? selectedOptions[0].label : ''
      this.cityCode = selectedOptions[1] ? selectedOptions[1].value : ''
      this.city = selectedOptions[1] ? selectedOptions[1].label : ''
      this.regionCode = selectedOptions[2] ? selectedOptions[2].value : ''
      this.region = selectedOptions[2] ? selectedOptions[2].label : ''
    },
}
  • 研究权限问题
mounted () {
  const userAgent = navigator.userAgent
  if (userAgent.indexOf('Edge') > -1) {
    this.$nextTick(() => {
      this.collapsed = !this.collapsed
      setTimeout(() => {
        this.collapsed = !this.collapsed
      }, 16)
    })
  }
  const oMenus = document.querySelector('.ant-menu.ant-menu-inline.ant-menu-root.ant-menu-dark')
  oMenus.addEventListener('click', (e) => {
    const url = e.target.getAttribute('href')
    if (url) {
      this.$router.push({
        path: url + '?t=' + new Date().getTime()
      })
    }
    setTimeout(() => {
      this.$router.push(url)
    }, 16)
    console.log('url====>', url)
  })
  // first update color
  // TIPS: THEME COLOR HANDLER!! PLEASE CHECK THAT!!
  if (process.env.NODE_ENV !== 'production' || process.env.VUE_APP_PREVIEW === 'true') {
    updateTheme(this.settings.primaryColor)
  }
},
  • 在 router-view 加上 key
<router-view :key="getPath"/>
  • 在computed
computed: {
  ...mapState({
    // 动态主路由
    mainMenu: state => state.permission.addRouters
  }),
  getPath () {
    return this.$route.fullPath
  }
},