diff --git a/manifest.json b/manifest.json index e3e30b9..c76963d 100644 --- a/manifest.json +++ b/manifest.json @@ -1,28 +1,31 @@ { - "name" : "mall-app-t", - "appid" : "__UNI__B201544", - "description" : "", - "versionName" : "1.0.0", - "versionCode" : "100", - "transformPx" : false, + "name": "mall-app-t", + "appid": "__UNI__B201544", + "description": "", + "versionName": "1.0.0", + "versionCode": "100", + "transformPx": false, /* 5+App特有相关 */ - "app-plus" : { - "usingComponents" : true, - "nvueStyleCompiler" : "uni-app", - "compilerVersion" : 3, - "splashscreen" : { - "alwaysShowBeforeRender" : true, - "waiting" : true, - "autoclose" : true, - "delay" : 0 + "app-plus": { + "usingComponents": true, + "nvueStyleCompiler": "uni-app", + "compilerVersion": 3, + "splashscreen": { + "alwaysShowBeforeRender": true, + "waiting": true, + "autoclose": true, + "delay": 0 + }, + "compatible": { + "ignoreVersion": true }, /* 模块配置 */ - "modules" : {}, + "modules": {}, /* 应用发布信息 */ - "distribute" : { + "distribute": { /* android打包配置 */ - "android" : { - "permissions" : [ + "android": { + "permissions": [ "", "", "", @@ -41,32 +44,32 @@ ] }, /* ios打包配置 */ - "ios" : {}, + "ios": {}, /* SDK配置 */ - "sdkConfigs" : {} + "sdkConfigs": {} } }, /* 快应用特有相关 */ - "quickapp" : {}, + "quickapp": {}, /* 小程序特有相关 */ - "mp-weixin" : { - "appid" : "wx64387dc8bba916ec", - "setting" : { - "urlCheck" : false + "mp-weixin": { + "appid": "wx64387dc8bba916ec", + "setting": { + "urlCheck": false }, - "usingComponents" : true + "usingComponents": true }, - "mp-alipay" : { - "usingComponents" : true + "mp-alipay": { + "usingComponents": true }, - "mp-baidu" : { - "usingComponents" : true + "mp-baidu": { + "usingComponents": true }, - "mp-toutiao" : { - "usingComponents" : true + "mp-toutiao": { + "usingComponents": true }, - "uniStatistics" : { - "enable" : false + "uniStatistics": { + "enable": false }, - "vueVersion" : "3" + "vueVersion": "3" } diff --git a/pages/index/product.vue b/pages/index/product.vue index 670eaa5..65ac93c 100644 --- a/pages/index/product.vue +++ b/pages/index/product.vue @@ -1,21 +1,43 @@ diff --git a/pages/index/redirect.vue b/pages/index/redirect.vue index cafbb83..b1157ac 100644 --- a/pages/index/redirect.vue +++ b/pages/index/redirect.vue @@ -1,15 +1,14 @@ diff --git a/pages/product/js/config.js b/pages/product/js/config.js index 3b99d36..f9b7475 100644 --- a/pages/product/js/config.js +++ b/pages/product/js/config.js @@ -8,3 +8,27 @@ export const SPEC_TYPE = [ value: true, }, ] + +// sku 相关属性校验 +export const SKU_RULE_CONFIG = [ + { + name: 'stock', + rule: (arg) => arg >= 0, + message: '商品库存必须大于等于 1 !!!', + }, + { + name: 'price', + rule: (arg) => arg >= 0.01, + message: '商品销售价格必须大于等于 0.01 元!!!', + }, + { + name: 'marketPrice', + rule: (arg) => arg >= 0.01, + message: '商品市场价格必须大于等于 0.01 元!!!', + }, + { + name: 'costPrice', + rule: (arg) => arg >= 0.01, + message: '商品成本价格必须大于等于 0.00 元!!!', + }, +] diff --git a/pages/product/js/sku.js b/pages/product/js/sku.js index 195cbdc..9560202 100644 --- a/pages/product/js/sku.js +++ b/pages/product/js/sku.js @@ -1,179 +1,277 @@ -import { ref, computed } from "vue"; -import { onLoad } from "@dcloudio/uni-app"; -import peach from "@/peach"; -import GoodsApi from "@/peach/api/trade/goods"; -import { SPEC_TYPE } from "./config"; +import { ref, computed } from 'vue' +import { onLoad } from '@dcloudio/uni-app' +import peach from '@/peach' +import GoodsApi from '@/peach/api/trade/goods' +import { SPEC_TYPE, SKU_RULE_CONFIG } from './config' -const pickerRef = ref(null); +const pickerRef = ref(null) // 多属性商品 sku 列表 -const skus = ref([]); +const skus = ref([]) -const propertyList = ref([]); +const propertyList = ref([]) -const goodsPropertyList = ref([]); +const goodsPropertyList = ref([]) -const propertyListRef = ref(null); +const propertyListRef = ref(null) -const canEdit = computed(() => peach.$store("trade").canEdit); +const canEdit = computed(() => peach.$store('trade').canEdit) const formData = ref({ - specType: true, - specText: SPEC_TYPE[0].label, -}); + specType: true, + specText: SPEC_TYPE[0].label, +}) async function showPropertyList() { - await getGoodsProperty(); - propertyListRef.value.onOpen(); + await getGoodsProperty() + propertyListRef.value.onOpen() } function onRDPickerConfirm(e) { - peach.$store("trade").specType = SPEC_TYPE[e.value[0]].value; - formData.value.specText = SPEC_TYPE[e.value[0]].label; - formData.value.specType = SPEC_TYPE[e.value[0]].value; + formData.value.specType = SPEC_TYPE[e.value[0]].value + formData.value.specText = SPEC_TYPE[e.value[0]].label + + // 如果商品规格不一致,则需要重新初始化 sku 列表 + initSku() + + peach.$store('trade').specType = SPEC_TYPE[e.value[0]].value } function pickerProperty() { - if (canEdit.value) { - let index = specType.value ? 1 : 0; - pickerRef.value.onOpen([index]); - } + if (canEdit.value) { + let index = formData.value.specType ? 1 : 0 + + console.log(index) + + pickerRef.value.onOpen([index]) + } } async function onPropertyConfirm(e) { - await getGoodsProperty(); - console.log(e); + await getGoodsProperty() + console.log(e) } function onConfirm() { - console.log(skus.value); + console.log(skus.value) } async function getGoodsProperty() { - let { data } = await GoodsApi.getHistoryProperty(); + let { data } = await GoodsApi.getHistoryProperty() - // 把 propertyList 中 id 相同的属性合并,并去重 - propertyList.value = peach.$store("trade").selectedProperty; + // 把 propertyList 中 id 相同的属性合并,并去重 + propertyList.value = peach.$store('trade').selectedProperty - console.log(propertyList.value); + console.log(propertyList.value) - // 根据已经选择数据,设置默认选中 - data.forEach((item) => { - // 判断属性是否已经选中 - let propertyParent = propertyList.value.find( - (sitem) => sitem?.id === item.id - ); + // 根据已经选择数据,设置默认选中 + data.forEach((item) => { + // 判断属性是否已经选中 + let propertyParent = propertyList.value.find((sitem) => sitem?.id === item.id) - item.checked = propertyParent ? true : false; + item.checked = propertyParent ? true : false - // 如果属性已经选中,查询子类中是否有选中 - if (item.checked) { - item.propertyValues.forEach((child) => { - let childResult = propertyParent?.children.some( - (schild) => schild === child.id - ); - child.checked = childResult ? true : false; - }); - } - }); + // 如果属性已经选中,查询子类中是否有选中 + if (item.checked) { + item.propertyValues.forEach((child) => { + let childResult = propertyParent?.children.some((schild) => schild === child.id) + child.checked = childResult ? true : false + }) + } + }) - goodsPropertyList.value = data; - console.log(goodsPropertyList.value); + goodsPropertyList.value = data + console.log(goodsPropertyList.value) } function changeSubProperty() { - // 修改子属性状态,需要同步更新 skus 的显示 - console.log(goodsPropertyList.value); - // 过滤父属性 checked 选项,深拷贝避免后面循环改变元数据内容 - let temp = JSON.parse( - JSON.stringify(goodsPropertyList.value.filter((item) => item.checked)) - ); - temp.forEach((item) => { - item.propertyValues = item.propertyValues.filter((child) => child.checked); - }); + // 修改子属性状态,需要同步更新 skus 的显示 + console.log(goodsPropertyList.value) + // 过滤父属性 checked 选项,深拷贝避免后面循环改变元数据内容 + let temp = JSON.parse(JSON.stringify(goodsPropertyList.value.filter((item) => item.checked))) + temp.forEach((item) => { + item.propertyValues = item.propertyValues.filter((child) => child.checked) + }) - let result = temp.map((item) => { - return item.propertyValues.map((child) => ({ - propertyId: item.id, - propertyName: item.name, - valueId: child.id, - valueName: child.name, - })); - }); + let result = temp.map((item) => { + return item.propertyValues.map((child) => ({ + propertyId: item.id, + propertyName: item.name, + valueId: child.id, + valueName: child.name, + })) + }) - let tempSkus = []; + let tempSkus = [] - for (let item of reduceArr(result)) { - let obj = { - picUrl: "", - barCode: "", - price: 0, - marketPrice: 0, - costPrice: 0, - stock: 0, - weight: 0, - volume: 0, - properties: item, - }; - tempSkus.push(obj); - } + for (let item of reduceArr(result)) { + let obj = { + picUrl: '', + barCode: '', + price: 0, + marketPrice: 0, + costPrice: 0, + stock: 0, + weight: 0, + volume: 0, + properties: item, + } + tempSkus.push(obj) + } - skus.value = tempSkus; + skus.value = tempSkus +} + +/** + * @author Ankkaya + * @description 新增商品初始化商品 sku + * @param {Type} - + * @returns {Type} + */ +function initSku() { + // 单规格 + if (!formData.value.specType) { + let obj = { + picUrl: '', + barCode: '', + price: 0, + marketPrice: 0, + costPrice: 0, + stock: 0, + weight: null, + volume: null, + properties: [ + { + propertyId: 0, + propertyName: '默认', + valueId: 0, + valueName: '默认', + }, + ], + } + + skus.value = [obj] + } else { + // 多规格 + skus.value = [] + } +} + +/** + * @author Ankkaya + * @description 确认属性 + * @param {Type} - + * @returns {Type} + */ +function submitProperty() { + try { + validateSku(skus.value) + peach.$store('trade').skus = skus.value + peach.$router.back() + } catch (e) { + console.log(skus.value) + console.log(e, '校验失败') + } +} + +function validateSku(skus) { + let warningInfo = '请检查商品各行相关属性配置,' + let validateStatue = true + let skusValue = skus ?? peach.$store('trade').skus + for (const sku of skusValue) { + for (const rule of SKU_RULE_CONFIG) { + const arg = getValue(sku, rule.name) + if (!rule.rule(arg)) { + validateStatue = false + warningInfo += rule.message + break + } + } + if (!validateStatue) { + uni.showToast({ + title: warningInfo, + icon: 'none', + duration: 4000, + }) + throw new Error(warningInfo) + } + } +} + +function getValue(obj, arg) { + const keys = arg.split('.') + let value = obj + for (const key of keys) { + if (value && typeof value === 'object' && key in value) { + value = value[key] + } else { + value = undefined + break + } + } + return value } function reduceArr(arr) { - return arr.reduce((acc, cur) => { - let tempAcc = []; + return arr.reduce((acc, cur) => { + let tempAcc = [] - if (acc.length < 1) { - cur.forEach((item, index) => { - if (tempAcc[index]) { - tempAcc[index].push(item); + if (acc.length < 1) { + cur.forEach((item, index) => { + if (tempAcc[index]) { + tempAcc[index].push(item) + } else { + tempAcc[index] = [item] + } + }) } else { - tempAcc[index] = [item]; - } - }); - } else { - acc.forEach((item, index) => { - cur.forEach((sitem, sindex) => { - tempAcc.push([...item, sitem]); - }); - }); + acc.forEach((item, index) => { + cur.forEach((sitem, sindex) => { + tempAcc.push([...item, sitem]) + }) + }) - if (cur.length < 1) { - tempAcc = acc; - } - } - return tempAcc; - }, []); + if (cur.length < 1) { + tempAcc = acc + } + } + return tempAcc + }, []) } -const specType = computed(() => peach.$store("trade").goodsInfo.specType); +const specType = computed(() => peach.$store('trade').goodsInfo?.specType || false) function initial() { - onLoad(() => { - formData.value.specType = specType.value ? true : false; - formData.value.specText = SPEC_TYPE[specType.value ? 1 : 0].label; - skus.value = peach.$store("trade").skus; - if (specType.value) { - getGoodsProperty(); - } - }); + onLoad(() => { + formData.value.specType = specType.value ? true : false + formData.value.specText = SPEC_TYPE[specType.value ? 1 : 0].label + skus.value = JSON.parse(JSON.stringify(peach.$store('trade').skus)) + // 如果新增商品 sku,并且是单规格,初始化 sku + + if (!skus.value) { + initSku() + } + if (specType.value) { + getGoodsProperty() + } + }) } export { - initial, - canEdit, - skus, - pickerRef, - pickerProperty, - onConfirm, - propertyListRef, - formData, - onRDPickerConfirm, - onPropertyConfirm, - propertyList, - showPropertyList, - goodsPropertyList, - changeSubProperty, -}; + initial, + canEdit, + skus, + pickerRef, + pickerProperty, + validateSku, + onConfirm, + submitProperty, + propertyListRef, + formData, + onRDPickerConfirm, + onPropertyConfirm, + propertyList, + showPropertyList, + goodsPropertyList, + changeSubProperty, +} diff --git a/pages/product/manageGoods.vue b/pages/product/manageGoods.vue index 0376f3e..dc1029b 100644 --- a/pages/product/manageGoods.vue +++ b/pages/product/manageGoods.vue @@ -1,187 +1,285 @@ diff --git a/pages/product/sku.vue b/pages/product/sku.vue index 7cb9ee9..507e249 100644 --- a/pages/product/sku.vue +++ b/pages/product/sku.vue @@ -1,45 +1,68 @@ diff --git a/peach/components/p-picker/p-picker.vue b/peach/components/p-picker/p-picker.vue index 829d16a..e906ec6 100644 --- a/peach/components/p-picker/p-picker.vue +++ b/peach/components/p-picker/p-picker.vue @@ -1,35 +1,47 @@ diff --git a/uni_modules/piaoyi-editor/changelog.md b/uni_modules/piaoyi-editor/changelog.md new file mode 100644 index 0000000..ff29b42 --- /dev/null +++ b/uni_modules/piaoyi-editor/changelog.md @@ -0,0 +1,10 @@ +## 1.1.0(2024-04-19) +解决不存在问题 +## 1.0.9(2024-02-04) +更新使用文档说明 +## 1.0.8(2023-12-28) +修改文档使用注意事项 +## 1.0.7(2023-12-05) +优化 +## 1.0.6(2023-12-05) +优化 diff --git a/uni_modules/piaoyi-editor/components/piaoyi-editor/color-picker.vue b/uni_modules/piaoyi-editor/components/piaoyi-editor/color-picker.vue new file mode 100644 index 0000000..ac6c30c --- /dev/null +++ b/uni_modules/piaoyi-editor/components/piaoyi-editor/color-picker.vue @@ -0,0 +1,784 @@ + + + + + diff --git a/uni_modules/piaoyi-editor/components/piaoyi-editor/iconfont.css b/uni_modules/piaoyi-editor/components/piaoyi-editor/iconfont.css new file mode 100644 index 0000000..9c5ab99 --- /dev/null +++ b/uni_modules/piaoyi-editor/components/piaoyi-editor/iconfont.css @@ -0,0 +1,123 @@ +@font-face { + font-family: "iconfont"; /* Project id 4040150 */ + src: url('iconfont.woff2?t=1682491617906') format('woff2'), + url('iconfont.woff?t=1682491617906') format('woff'), + url('iconfont.ttf?t=1682491617906') format('truetype'); +} + +.iconfont { + font-family: "iconfont" !important; + font-size: 16px; + font-style: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.icon-checklist:before { + content: "\e600"; +} + +.icon-zitiyanse:before { + content: "\e646"; +} + +.icon-formatheader1:before { + content: "\e860"; +} + +.icon-formatheader2:before { + content: "\e861"; +} + +.icon-undo:before { + content: "\e787"; +} + +.icon-redo:before { + content: "\e788"; +} + +.icon-indent:before { + content: "\e7f3"; +} + +.icon-outdent:before { + content: "\e7f4"; +} + +.icon-zitijiacu:before { + content: "\ec83"; +} + +.icon-zuoyouduiqi:before { + content: "\ec87"; +} + +.icon-Character-Spacing:before { + content: "\ed91"; +} + +.icon-format:before { + content: "\e6da"; +} + +.icon-font-size:before { + content: "\e7b9"; +} + +.icon-duanhouju:before { + content: "\e61a"; +} + +.icon-duanqianju:before { + content: "\e61b"; +} + +.icon-shanchuxian:before { + content: "\e602"; +} + +.icon-charutupian:before { + content: "\e603"; +} + +.icon-fengexian:before { + content: "\e60e"; +} + +.icon-juzhongduiqi:before { + content: "\e620"; +} + +.icon-wuxupailie:before { + content: "\e63e"; +} + +.icon-youduiqi:before { + content: "\e64b"; +} + +.icon-youxupailie:before { + content: "\e64c"; +} + +.icon-zitixiahuaxian:before { + content: "\e657"; +} + +.icon-zitixieti:before { + content: "\e658"; +} + +.icon-zuoduiqi:before { + content: "\e65a"; +} + +.icon-LineHeight:before { + content: "\e624"; +} + +.icon-editor-background-color:before { + content: "\e829"; +} + diff --git a/uni_modules/piaoyi-editor/components/piaoyi-editor/iconfont.ttf b/uni_modules/piaoyi-editor/components/piaoyi-editor/iconfont.ttf new file mode 100644 index 0000000..712351d Binary files /dev/null and b/uni_modules/piaoyi-editor/components/piaoyi-editor/iconfont.ttf differ diff --git a/uni_modules/piaoyi-editor/components/piaoyi-editor/iconfont.woff b/uni_modules/piaoyi-editor/components/piaoyi-editor/iconfont.woff new file mode 100644 index 0000000..2689590 Binary files /dev/null and b/uni_modules/piaoyi-editor/components/piaoyi-editor/iconfont.woff differ diff --git a/uni_modules/piaoyi-editor/components/piaoyi-editor/iconfont.woff2 b/uni_modules/piaoyi-editor/components/piaoyi-editor/iconfont.woff2 new file mode 100644 index 0000000..be94f85 Binary files /dev/null and b/uni_modules/piaoyi-editor/components/piaoyi-editor/iconfont.woff2 differ diff --git a/uni_modules/piaoyi-editor/components/piaoyi-editor/piaoyi-editor.vue b/uni_modules/piaoyi-editor/components/piaoyi-editor/piaoyi-editor.vue new file mode 100644 index 0000000..857f4bd --- /dev/null +++ b/uni_modules/piaoyi-editor/components/piaoyi-editor/piaoyi-editor.vue @@ -0,0 +1,516 @@ + + + + + diff --git a/uni_modules/piaoyi-editor/package.json b/uni_modules/piaoyi-editor/package.json new file mode 100644 index 0000000..9f75a32 --- /dev/null +++ b/uni_modules/piaoyi-editor/package.json @@ -0,0 +1,15 @@ +{ + "id": "piaoyi-editor", + "name": "多功能富文本编辑器", + "displayName": "多功能富文本编辑器", + "version": "1.1.0", + "description": "富文本编辑器,内置上传图片以及更改颜色, 多样化等功能", + "keywords": [ + "富文本编辑器", + "上传图片", + "字体颜色" + ], + "dcloudext": { + "type": "component-vue" + } +} \ No newline at end of file diff --git a/uni_modules/piaoyi-editor/readme.md b/uni_modules/piaoyi-editor/readme.md new file mode 100644 index 0000000..6c26cf5 --- /dev/null +++ b/uni_modules/piaoyi-editor/readme.md @@ -0,0 +1,101 @@ +### piaoyiEditor 富文本编辑器 + +**使用方法:** + +``` + + + + + + +``` + +#### 事件说明 + +1、**使用上传图片功能时需要注意查看代码里面的uni.uploadFile方法,因为它的返回值取决于自己后端接口的值,所以可以根据实际情况就行更改即可** + +| 事件名 | 返回值 | 描述 | +| :---------: | :----: | :------------: | +| @saveContens | {html: html片段, length: html长度} | 文本框内容回调 | + +#### Prop + +| 参数名称 | 默认值 | 描述 | +| -------- | ------------------------------ | +| maxlength| 300 | 输入最大长度 | +| readOnly | false | 是否只读 | +| api | 空 | 上传图片接口地址 | +| photoUrl | 空 | 服务器图片域名或者ip | +| name | 'file' | 上传图片接口的key | +| values | '' | 富文本编辑器默认值 | + +### 注:近期收到使用用户反馈,存在以下四个问题(如有好的建议,期待私信,谢谢) + +1、当组件在页面中部或者底部的时候,进入页面,页面会自动滚动到富文本编辑器的区域 +属于正常现象; +官网文档有这么一句话:编辑器聚焦时页面会被上推,系统行为以保证编辑区可见; +作者建议这种情况,进入页面初始设置富文本编辑器为只读,然后页面滚动到一定距离的时候取消这个只读; + +2、组件粘贴文字出现软键盘闪烁,导致文字粘贴不了 +目前暂未发现解决方法,本插件是在官方的editor基础上开发的,这个组件存在这个问题; +经测试长按出现粘贴后,手不松开滑动到粘贴字样上就不会出现闪烁,然后松开手,点击粘贴就可以; + +3、有些上传图片接口是需要token的,接口需要token的话可以在组件内搜索uni.uploadFile,加上headers头部参数 + +4、H5有时候会出现插件异常情况,不要慌,查看[editor组件官网](https://uniapp.dcloud.net.cn/component/editor.html)官网下的注意事项(原话是:H5 端需要动态引入 quill.min.js、image-resize.min.js 依赖,默认情况下浏览器会从 unpkg.com 加载。如果依赖加载较慢或失败,uni-app 建议使用通过测试的 js 依赖保证效果一致,访问 github.com 或者 gitee.com 选择下载。可以放入 static 目录进行托管,或者使用 CDN 服务商。为了保证服务的稳定性,推荐开发者将所有前端资源使用 uniCloud 前端网页托管 服务进行托管,然后在 自定义模板 的 head 标签内引入相关 js 依赖。) + + (1)上面H5这种情况我推荐一种方法进行测试:把所需要的静态资源下载到本地,在APP.vue里面的onLunch下进行dom操作插入这些静态资源 + (2)使用官方提供的自定义模板(hx新建项目的Hello uni-app模板里面有示例):使用官方的自定义模板引入静态资源,亲测引入静态资源成功了(即引入js和css都是本地的静态资源,不再请求远程CDN文件),但是原来页面样式全丢失了,目前还没有找到问题所在没有成功的朋友欢迎加群交流一下,谢谢 + +### 可接定制化组件开发 +### 右侧有本人代表作小程序二维码,可以扫码体验 +### 如使用过程中有问题或有一些好的建议,欢迎加QQ群互相学习交流:120594820 \ No newline at end of file