哇,好久没有记录自己写代码的总结了,今天记录下,点赞,订阅,转发,感谢各位老铁的支持与厚爱。
电商项目的总结
给你七年时间,你会干什么?我想不出我会干什么耶,我只有确定的一件事情就是我会全国各地跑一遍
装饰器教程
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
- 登录跳转问题,不需要 vuex 存储状态了,因为刷新 vuex 数据丧失了,需要使用缓存机制
- tabbar 页面与非tabbar页跳转互动问题
- 订单页面,需要提示用户去登录的,
- 商品列表,单个列表项的删除功能,添加,减少,清空操作,用户操作,用户体验
- 商品规格不应该弄一个管理,而是应该让用户在添加商品的过程自己去添加
- 多测试
- 登录或许可以写成一个公用的组件
- 删除一些无用的代码
- 重复点击菜单栏项,需要刷新页面
- 资源共享的功能
- 打印机打印的问题
- 拖拽插件
- 添加小标签问题
- 小类,大类一般是怎么添加的
- 研究三级联动组件
- 环境配置问题,提取公共的 url
- 上传组件需要添加loading状态
- 图片需要懒加载
- vue中的图片加载与显示默认图片
- vue项目设置img标签的默认图片
- 基于阿里Ant Design of Vue的上传组件二次封装
- 301 logout 重定向到登录页面
- 提取 store 几个组件到一个 home/store 目录,方便管理
- 402 非法类别
- 写着,写着麻烦,要停下来想想有没有更好的方式
- 基于阿里Ant Design of Vue的上传组件二次封装
- Cookie、 session、localStorage 和sessionStorage的存储区别
- 添加店铺描述
- 为什么要这么抽取,为什么要这么实现
- 像产品思维那样转变
- 领导是结果导向的,员工是过程导向的,我们应该像结果导向过渡,那么要怎么做
- 为什么要抽丝剥茧,做到这一步的理由是什么,为什么要这么做
- 提取公共的样式
- 添加按钮组件修改宽高度文图
- 提取公共的颜色,字体大小变量
- 提取 js 中的常量,比如数字
- 直接定义变量直接可以全局使用
- 在vue中自动生成文件以及自动引入component,router、vuex按模块划分
- 瀑布流组件
- 将 StoreModal, StoreCard 抽取到 Store 目录
- 商品模块使用卡片式模块,方便定位
- 可增加,删除,编辑的树组件,数据在组件里面处理,只需要将处理之后的数据暴露出来就行了
- 使用级联的方式去搜索对应的产品 级联的数据应该在里面处理,只需要将处理之后的数据暴露出来就行了
- 商品列表组件,只需要将处理之后的数据暴露出来就行了
- 滚动定位,https://www.cnblogs.com/haonanZhang/p/9517636.html
- 低代码快速搭建完整商品列表页
- que-etc/resize-observer-polyfill
- 前端虚拟列表的实现原理
- ant-design-vue锚点
- 多个数组笛卡尔积-js算法
- js 生成笛卡尔积
- 商品规格SKU笛卡尔积计算
- Erupt Framework ? 通用后台管理框架
- 国内低代码平台从业者交流
- 低代码平台, 可视化编辑器,单手打代码,解放你的双手
- 登录详解(VUE前端) · 低代码开发平台文档 · 看云
- 微前端项目实战
- Angular-HMR
- 今天的目标是把查询页面搞出来,规格添加页面
- 今天对接规格创建,添加,修改
- 今晚把基本商品添加页,图片上传基本信息,搞完
- 今晚处理,单规格,多规格问题
- 多规格表格上传,删除处理要回现处理规格,单规格(6-8)
- 分类也要点击弹框,可以修改添加,编辑
- 树组件有个隐藏的风险,就是可能误添加上下级关系,给出添加,修改各个提示
三种价格设置
代码语言:javascript复制<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>
代码语言:javascript复制function cartesianProductOf(...args) {
return args.reduce(
(total, current) => {
let ret = [];
total.forEach(a => {
current.forEach(b => {
ret.push(a.concat([b]));
});
});
return ret;
},
[
[]
]
);
}
代码语言:javascript复制// main.js
const components = require('./components/index')
for (let componentName in components) {
Vue.component(componentName, components[componentName])
}
代码语言:javascript复制// 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'))
},
}
代码语言:javascript复制// 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
代码语言:javascript复制// entryTemplate.js
module.exports = {
entryTemplate: (compoenntName) => {
return `
import ${compoenntName} from './src'
export default ${compoenntName}
`
}
}
代码语言:javascript复制// 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()
})
}
}
代码语言:javascript复制// 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>
`
}
}
代码语言:javascript复制/*第一层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';//路径
}
代码语言:javascript复制 <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>
代码语言:javascript复制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
}
},