diff --git a/package-lock.json b/package-lock.json index a6191b0..58865b4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,11 +15,13 @@ "@vueuse/core": "^12.7.0", "@wangeditor/editor": "^5.1.23", "@wangeditor/editor-for-vue": "^5.1.12", + "@yipai-front-end/lib": "^0.0.27", "axios": "^1.7.9", "css-color-function": "^1.3.3", "echarts": "^5.6.0", "element-plus": "^2.9.4", "highlight.js": "^11.11.1", + "jsonp": "^0.2.1", "lodash": "^4.17.21", "lodash-es": "^4.17.21", "nprogress": "^0.2.0", @@ -27,7 +29,9 @@ "vue": "^3.5.13", "vue-clipboard3": "^2.0.0", "vue-echarts": "^6.7.3", + "vue-qqmap": "^1.1.1", "vue-router": "^4.5.0", + "vue3-sku-form": "^1.0.1", "vuedraggable": "^4.1.0" }, "devDependencies": { @@ -2772,6 +2776,19 @@ "node": ">= 8.0.0" } }, + "node_modules/@rollup/pluginutils/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.40.2", "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.2.tgz", @@ -3929,6 +3946,12 @@ "snabbdom": "^3.1.0" } }, + "node_modules/@yipai-front-end/lib": { + "version": "0.0.27", + "resolved": "https://registry.npmmirror.com/@yipai-front-end/lib/-/lib-0.0.27.tgz", + "integrity": "sha512-X5llKyS/sImpDOQKkZU/BU2HB1N4mqFdAmAKoogO2I3gTHK65/KwvvfoqF/cLYVvnyfjFDONkPGfGwaUNWfKeg==", + "license": "ISC" + }, "node_modules/acorn": { "version": "8.14.1", "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.14.1.tgz", @@ -4036,6 +4059,19 @@ "node": ">= 8" } }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmmirror.com/arg/-/arg-5.0.2.tgz", @@ -4455,7 +4491,6 @@ "version": "1.0.4", "resolved": "https://registry.npmmirror.com/call-bound/-/call-bound-1.0.4.tgz", "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "dev": true, "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" @@ -4757,7 +4792,6 @@ "version": "3.42.0", "resolved": "https://registry.npmmirror.com/core-js/-/core-js-3.42.0.tgz", "integrity": "sha512-Sz4PP4ZA+Rq4II21qkNqOEDTDrCvcANId3xpIgB34NDkWc3UduWj2dqEtN9yZIq8Dk3HyPI33x9sqqU5C8sr0g==", - "dev": true, "hasInstallScript": true, "funding": { "type": "opencollective", @@ -7547,6 +7581,29 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jsonp": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/jsonp/-/jsonp-0.2.1.tgz", + "integrity": "sha512-pfog5gdDxPdV4eP7Kg87M8/bHgshlZ5pybl+yKxAnCZ5O7lCIn7Ixydj03wOlnDQesky2BPyA91SQ+5Y/mNwzw==", + "dependencies": { + "debug": "^2.1.3" + } + }, + "node_modules/jsonp/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/jsonp/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz", @@ -7837,6 +7894,19 @@ "node": ">=8.6" } }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", @@ -8256,7 +8326,6 @@ "version": "1.13.4", "resolved": "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.4.tgz", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -8514,12 +8583,13 @@ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, + "license": "MIT", "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -8906,6 +8976,21 @@ "node": ">=6" } }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmmirror.com/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/quansync": { "version": "0.2.10", "resolved": "https://registry.npmmirror.com/quansync/-/quansync-0.2.10.tgz", @@ -9550,7 +9635,6 @@ "version": "1.1.0", "resolved": "https://registry.npmmirror.com/side-channel/-/side-channel-1.1.0.tgz", "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "dev": true, "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", @@ -9569,7 +9653,6 @@ "version": "1.0.0", "resolved": "https://registry.npmmirror.com/side-channel-list/-/side-channel-list-1.0.0.tgz", "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "dev": true, "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" @@ -9585,7 +9668,6 @@ "version": "1.0.1", "resolved": "https://registry.npmmirror.com/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "dev": true, "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -9603,7 +9685,6 @@ "version": "1.0.2", "resolved": "https://registry.npmmirror.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "dev": true, "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -10539,6 +10620,19 @@ "node": ">=10.13.0" } }, + "node_modules/tailwindcss/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/tailwindcss/node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz", @@ -10622,18 +10716,6 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/to-object-path": { "version": "0.3.0", "resolved": "https://registry.npmmirror.com/to-object-path/-/to-object-path-0.3.0.tgz", @@ -11076,18 +11158,6 @@ "@types/estree": "^1.0.0" } }, - "node_modules/unimport/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/union-value": { "version": "1.0.1", "resolved": "https://registry.npmmirror.com/union-value/-/union-value-1.0.1.tgz", @@ -11158,18 +11228,6 @@ } } }, - "node_modules/unplugin-auto-import/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/unplugin-utils": { "version": "0.2.4", "resolved": "https://registry.npmmirror.com/unplugin-utils/-/unplugin-utils-0.2.4.tgz", @@ -11186,18 +11244,6 @@ "url": "https://github.com/sponsors/sxzz" } }, - "node_modules/unplugin-utils/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/unplugin-vue-components": { "version": "28.5.0", "resolved": "https://registry.npmmirror.com/unplugin-vue-components/-/unplugin-vue-components-28.5.0.tgz", @@ -11274,6 +11320,19 @@ } } }, + "node_modules/unplugin-vue-components/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/unplugin-vue-components/node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz", @@ -11286,18 +11345,6 @@ "node": ">=8.10.0" } }, - "node_modules/unplugin/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/unset-value": { "version": "1.0.0", "resolved": "https://registry.npmmirror.com/unset-value/-/unset-value-1.0.0.tgz", @@ -11633,18 +11680,6 @@ "sourcemap-codec": "^1.4.8" } }, - "node_modules/vite/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/vscode-uri": { "version": "3.1.0", "resolved": "https://registry.npmmirror.com/vscode-uri/-/vscode-uri-3.1.0.tgz", @@ -11818,6 +11853,50 @@ "node": ">=10" } }, + "node_modules/vue-jsonp": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/vue-jsonp/-/vue-jsonp-2.1.0.tgz", + "integrity": "sha512-kezmjaAcMWdieO3tWxniC+82DitYUYjR1e2GsWIKHCTf+zhWUt2nPhN3dnmnAVhDQ+po3BspM7sKjiQaIhijUg==", + "license": "MIT" + }, + "node_modules/vue-qqmap": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/vue-qqmap/-/vue-qqmap-1.1.1.tgz", + "integrity": "sha512-fL58MO31pmXuADRc8eYYPdLTNl7b68pP0YbyR3CLja9D5PeFv7cF4K5DOR2mOb/lCHjRVUUF+Ft5f6KVDnPUog==", + "license": "MIT", + "dependencies": { + "axios": "^0.21.1", + "core-js": "^3.6.5", + "lodash-es": "^4.17.21", + "qs": "^6.10.1", + "typescript": "^4.3.5", + "vue": "^3.2.0", + "vue-jsonp": "^2.0.0", + "vue-qqmap": "^1.0.9" + } + }, + "node_modules/vue-qqmap/node_modules/axios": { + "version": "0.21.4", + "resolved": "https://registry.npmmirror.com/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, + "node_modules/vue-qqmap/node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmmirror.com/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, "node_modules/vue-router": { "version": "4.5.1", "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.5.1.tgz", @@ -11848,6 +11927,19 @@ "typescript": ">=5.0.0" } }, + "node_modules/vue3-sku-form": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/vue3-sku-form/-/vue3-sku-form-1.0.1.tgz", + "integrity": "sha512-K54a7pYk6VKVhOpB/aG7syM9s4WXnAx4jdmbu2WUs0CoQDvvZ1TJONeybDyJB61k1Vv9UmGUhuc0PjZO194IEw==", + "license": "MIT", + "dependencies": { + "vue": "^3.3.4" + }, + "peerDependencies": { + "element-plus": "^2.3.0", + "vue-router": "^4.0.0" + } + }, "node_modules/vuedraggable": { "version": "4.1.0", "resolved": "https://registry.npmmirror.com/vuedraggable/-/vuedraggable-4.1.0.tgz", diff --git a/package.json b/package.json index 428a993..d87ca2e 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@vueuse/core": "^12.7.0", "@wangeditor/editor": "^5.1.23", "@wangeditor/editor-for-vue": "^5.1.12", + "@yipai-front-end/lib": "^0.0.27", "axios": "^1.7.9", "css-color-function": "^1.3.3", "echarts": "^5.6.0", @@ -31,6 +32,7 @@ "vue-echarts": "^6.7.3", "vue-qqmap": "^1.1.1", "vue-router": "^4.5.0", + "vue3-sku-form": "^1.0.1", "vuedraggable": "^4.1.0" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 715bced..4966122 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -24,8 +24,8 @@ importers: specifier: ^5.1.23 version: 5.1.23 '@wangeditor/editor-for-vue': - specifier: ^5.1.12 - version: 5.1.12(@wangeditor/editor@5.1.23)(vue@3.5.14(typescript@5.8.3)) + specifier: ^1.0.2 + version: 1.0.2(@wangeditor/editor@5.1.23)(vue@3.5.14(typescript@5.8.3)) axios: specifier: ^1.7.9 version: 1.9.0 @@ -1444,11 +1444,11 @@ packages: slate: ^0.72.0 snabbdom: ^3.1.0 - '@wangeditor/editor-for-vue@5.1.12': - resolution: {integrity: sha512-0Ds3D8I+xnpNWezAeO7HmPRgTfUxHLMd9JKcIw+QzvSmhC5xUHbpCcLU+KLmeBKTR/zffnS5GQo6qi3GhTMJWQ==} + '@wangeditor/editor-for-vue@1.0.2': + resolution: {integrity: sha512-BOENvAXJVtVXlE2X50AAvjV82YlCUeu5cbeR0cvEQHQjYtiVnJtq7HSoj85r2kTgGouI5OrpJG9BBEjSjUSPyA==} peerDependencies: '@wangeditor/editor': '>=5.1.0' - vue: ^3.0.5 + vue: ^2.6.14 '@wangeditor/editor@5.1.23': resolution: {integrity: sha512-0RxfeVTuK1tktUaPROnCoFfaHVJpRAIE2zdS0mpP+vq1axVQpLjM8+fCvKzqYIkH0Pg+C+44hJpe3VVroSkEuQ==} @@ -5345,7 +5345,7 @@ snapshots: slate-history: 0.66.0(slate@0.72.8) snabbdom: 3.6.2 - '@wangeditor/editor-for-vue@5.1.12(@wangeditor/editor@5.1.23)(vue@3.5.14(typescript@5.8.3))': + '@wangeditor/editor-for-vue@1.0.2(@wangeditor/editor@5.1.23)(vue@3.5.14(typescript@5.8.3))': dependencies: '@wangeditor/editor': 5.1.23 vue: 3.5.14(typescript@5.8.3) diff --git a/src/api/goods.ts b/src/api/goods.ts index 4455bfb..1c00a4a 100644 --- a/src/api/goods.ts +++ b/src/api/goods.ts @@ -28,4 +28,9 @@ export function apiGoodsDetail(params: any) { // 商品分类列表 export function checkCategory() { return request.post({ url: '/goodsCategory/checkCategory' }) -} \ No newline at end of file +} + +// 商品上传图片 +export function uploadImage(params: any) { + return request.post({ url: '/upload/image', params }) +} diff --git a/src/components/editor/index.vue b/src/components/editor/index.vue index f50a236..90291bf 100644 --- a/src/components/editor/index.vue +++ b/src/components/editor/index.vue @@ -1,25 +1,9 @@ + + diff --git a/src/views/goods/components/index.js b/src/views/goods/components/index.js new file mode 100644 index 0000000..db3bc34 --- /dev/null +++ b/src/views/goods/components/index.js @@ -0,0 +1,25 @@ +import SkuForm from './SkuForm.vue' + +// 版本信息 +const version = '1.0.0' + +// 组件安装函数 +const install = (app) => { + app.component('SkuForm', SkuForm) +} + +// 组件库对象 +const SkuFormLib = { + version, + install, + SkuForm +} + +// Vue插件安装函数 +SkuForm.install = app => { + app.component('SkuForm', SkuForm) +} + +// 导出方式支持 ES Module、CommonJS 和全局变量 +export { version, SkuForm } +export default SkuFormLib diff --git a/src/views/goods/components/sku.vue b/src/views/goods/components/sku.vue new file mode 100644 index 0000000..5c64540 --- /dev/null +++ b/src/views/goods/components/sku.vue @@ -0,0 +1,307 @@ + + + + + diff --git a/src/views/goods/components/type.d.ts b/src/views/goods/components/type.d.ts new file mode 100644 index 0000000..464d8a7 --- /dev/null +++ b/src/views/goods/components/type.d.ts @@ -0,0 +1,39 @@ +/** + * sku表格字段 + */ +export type skuType = { + attributeValue: string + id?: string + marketPrice: string + price: string + specificationBarCode: string + stock: string + thumbnailUrl: string +} + +/** + * 销售属性类型 + */ +export type skuAttrItemType = { + /** + * 是否上传图片 + */ + isAddImage: boolean + /** + * 名称 + */ + title: string + /** + * 具体数据 + */ + values: { + /** + * 属性图片 + * */ + thumbnailUrl?: string + /** + * 属性名称 + */ + attributeValue: string + }[] +} diff --git a/src/views/goods/edit copy.vue b/src/views/goods/edit copy.vue index 62f4266..7f4db8c 100644 --- a/src/views/goods/edit copy.vue +++ b/src/views/goods/edit copy.vue @@ -15,26 +15,26 @@
-
+
- +
-
+
- +
- +
@@ -47,18 +47,17 @@ - - + 统一规格 多规格 -
+
@@ -267,8 +191,10 @@ import type { FormInstance } from 'element-plus' import Popup from '@/components/popup/index.vue' import { apiGoodsAdd, apiGoodsEdit, apiGoodsDetail, checkCategory } from '@/api/goods' -import { timeFormat } from '@/utils/util' import type { PropType } from 'vue' +import feedback from '@/utils/feedback' +import type { AnyAaaaRecord } from 'dns' + defineProps({ dictData: { type: Object as PropType>, @@ -279,9 +205,7 @@ const emit = defineEmits(['success', 'close']) const formRef = shallowRef() const popupRef = shallowRef>() const mode = ref('add') -const goodsSpec = ref(1) -let showSpec = ref(false) -let specValue = ref("") +const goodsSpec = ref(2) // 弹窗标题 const popupTitle = computed(() => { @@ -300,55 +224,58 @@ const formData = reactive({ remark: '', image: '', goods_image: '', - + video: '', + poster: '', one_price: '', one_cost_price: '', one_stock: '', + one_market_price: '', + one_spec_image: '', + one_volume: '', + one_weight: '', + one_bar_code: '', + spec_name: '', + spec_type: 1, + content: '', + sales_sum: 0, + click_count: 0, + virtual_sales_sum: '', + virtual_click: '', + stock_warn: 0, + is_show_stock: 1, + is_express: 1, + is_integral: 0, + give_integral_type: 0, + give_integral_num: '', + give_integral_ratio: '', + status: 0, brand_id: '', supplier_id: '', - status: '', - - video: '', - one_spec_image: '', - poster: '', - content: '', sort: '', - sales_sum: '', - virtual_sales_sum: '', - click_count: '', - virtual_click: '', - spec_type: '', - max_price: '', - min_price: '', - market_price: '', - stock: '', - stock_warn: '', - is_show_stock: '', - free_shipping_type: '', - free_shipping: '', - free_shipping_template_id: '', - is_commission: '', - first_ratio: '', - second_ratio: '', - three_ratio: '', - is_share_bouns: '', - region_ratio: '', - shareholder_ratio: '', + max_price: 0, + min_price: 0, + market_price: 0, + free_shipping_type: 0, + free_shipping: 0, + free_shipping_template_id: 0, + is_commission: 0, + first_ratio: 0, + second_ratio: 0, + three_ratio: 0, + is_share_bouns: 0, + region_ratio: 0, + shareholder_ratio: 0, is_new: '', is_best: '', is_like: '', - is_team: '', - is_integral: '', - is_member: '', - give_integral_type: '', - give_integral: '', - del: '', - is_express: '', + is_team: 0, + is_member: 0, + give_integral: 0, is_selffetch: '', }) -// 表单验证 +// // 表单验证 const formRules = reactive({ name: [{ required: true, @@ -360,60 +287,20 @@ const formRules = reactive({ message: '请选择商品分类', trigger: ['blur'] }], - // status: [{ - // required: true, - // message: '请输入商品状态:-1-回收站;0-下架;1-上架', - // trigger: ['blur'] - // }], - // image: [{ - // required: true, - // message: '请输入商品主图', - // trigger: ['blur'] - // }], - // is_show_stock: [{ - // required: true, - // message: '请输入是否显示库存:1-是;0-否', - // trigger: ['blur'] - // }], - // free_shipping_type: [{ - // required: true, - // message: '请输入运费类型:1-包邮;2-统一运费;3-运费模板', - // trigger: ['blur'] - // }], - // is_commission: [{ - // required: true, - // message: '请输入分销佣金:1-开启;0-不开启', - // trigger: ['blur'] - // }], - // is_share_bouns: [{ - // required: true, - // message: '请输入区域股东分红:1-开启;0-不开启', - // trigger: ['blur'] - // }], - // is_team: [{ - // required: true, - // message: '请输入是否开启拼团[0=否, 1=是]', - // trigger: ['blur'] - // }], - // is_integral: [{ - // required: true, - // message: '请输入积分抵扣:1-开启;0-不开启', - // trigger: ['blur'] - // }], - // is_member: [{ - // required: true, - // message: '请输入会员价:1-开启;0-不开启', - // trigger: ['blur'] - // }], - // give_integral_type: [{ - // required: true, - // message: '请输入赠送积分类型:0-不赠送;1-赠送固定积分;2-按比例赠送积分', - // trigger: ['blur'] - // }] + image: [{ + required: true, + message: '请上传商品主图', + trigger: ['blur'] + }], + goods_image: [{ + required: true, + message: '请上传商品轮播图', + trigger: ['blur'] + }], }) onMounted(() => { - category() + getCategory() }); // 获取详情 @@ -430,15 +317,60 @@ const getDetail = async (row: Record) => { const data = await apiGoodsDetail({ id: row.id }) - setFormData(data) + + // 提取 abs_image 并重新赋值给 goods_image + if (Array.isArray(data['base']['goods_image'])) { + data['base']['goods_image'] = data['base']['goods_image'].map(item => item.abs_image) + } + + setFormData(data['base']) + // 编辑--重新组成数据 + const { base, item, spec } = data + if (base.spec_type == 1) { + // 单规格 + formData['one_price'] = item[0].price + formData['one_cost_price'] = item[0].cost_price + formData['one_stock'] = item[0].stock + formData['one_volume'] = item[0].volume + formData['one_weight'] = item[0].weight + formData['one_bar_code'] = item[0].bar_code + } else if (base.spec_type == 2) { + // 多规格 + // 1. 渲染规格名和规格值 + specs.value = spec.map((s: any) => ({ + name: s.name, + valuesStr: s.values.map((v: any) => v.value).join('\n'), + values: s.values.map((v: any) => v.value) + })) + // 2. 渲染规格表格 + skuTable.value = item.map((it: any) => { + const row: any = { + price: it.price, + cost_price: it.cost_price, + stock: it.stock + } + // 动态添加 spec0, spec1, spec2 + if (it.spec_value_str) { + it.spec_value_str.split(',').forEach((v: string, idx: number) => { + row[`spec${idx}`] = v + }) + } + return row + }) + + syncSpecToFormData() + } + + // 设置分类 + selectFirstCategory(data.first_category_id) + selectSecondCategory(data.second_category_id) } -// -// 创建分类列表 +// let goodsCategory = reactive([]) let firstCategory = reactive([]) let secondCategory = reactive([]) let thirdCategory = reactive([]) -const category = async () => { +const getCategory = async () => { const res = await checkCategory() goodsCategory = res firstCategory = res.filter((item: { pid: number }) => { @@ -477,10 +409,30 @@ const selectSecondCategory = (id: number) => { // 提交按钮 const handleSubmit = async () => { - // await formRef.value?.validate() - const data = { ...formData, } - console.log("data>>>", data); - return false; + await formRef.value?.validate() + const data = { ...formData } + if (data.spec_type == 1 && !data.one_price && !data.one_cost_price && !data.one_stock) { + feedback.notifyError('请填写商品规格') + return false + } + + data.price = skuTable.value.map(item => item.price) + data.cost_price = skuTable.value.map(item => item.cost_price) + data.stock = skuTable.value.map(item => item.stock) + data.spec_value_str = skuTable.value.map(item => { + const arr = [] + if ('spec0' in item) arr.push(item.spec0) + if ('spec1' in item) arr.push(item.spec1) + if ('spec2' in item) arr.push(item.spec2) + return arr.join(',') + }) + + if (data.spec_type == 2 && !data.price && !data.cost_price && !data.stock && !data.spec_value_str) { + feedback.notifyError('请填写商品多规格字段') + return false + } + + mode.value == 'edit' ? await apiGoodsEdit(data) : await apiGoodsAdd(data) @@ -499,33 +451,141 @@ const handleClose = () => { emit('close') } +// 多规格相关 +// const specs = ref([ +// { name: '', valuesStr: '', values: [] } +// ]) +const specs = ref([]) +const skuTable = ref([]) -// 添加规格值 -const addSpecValue = () => { - showSpec.value = true +const onSpecNameChange = () => { + syncSpecToFormData() } -// 关闭规格值弹窗 -const closeSpecPopup = () => { - specValue.value = '' - showSpec.value = false -} - -const confirmSpec = () => { - if (specValue.value) { - let specs = specValue.value.split('\n'); - for (let i in specs) { - specs[i] = specs[i].trim(); - } - +// 添加规格项(最多3个) +const addSpec = () => { + if (specs.value.length < 3) { + specs.value.push({ name: '', valuesStr: '', values: [] }) + syncSpecToFormData() } } +// 删除规格项 +const removeSpec = (idx: number) => { + specs.value.splice(idx, 1) + updateSkuTable() + syncSpecToFormData() +} + +// 规格值变更 +const onSpecValueChange = (idx: number) => { + const spec = specs.value[idx] + spec.values = spec.valuesStr + .split('\n') + .map(v => v.trim()) + .filter(v => v) + updateSkuTable() + syncSpecToFormData() +} + +// 计算所有规格组合(笛卡尔积) +function cartesianProduct(arr: any[][]) { + if (arr.length === 0) return [] + return arr.reduce((a, b) => + a.flatMap(d => b.map(e => [].concat(d, e))) + ) +} + +// 同步规格数据到formData +const syncSpecToFormData = () => { + formData.spec_name = specs.value.map(item => item.name) + formData.spec_values = specs.value.map(item => item.valuesStr.replace(/\n/g, ',')) +} + + +// 更新SKU表格 +const updateSkuTable = () => { + const valueArr = specs.value.map(s => s.values) + if (valueArr.some(arr => arr.length === 0)) { + skuTable.value = [] + return + } + + let result: any[] = [] + if (valueArr.length === 1) { + result = valueArr[0].map(v1 => { + const old = skuTable.value.find(row => row.spec0 === v1) + return { + spec0: v1, + price: old ? old.price : '', + cost_price: old ? old.cost_price : '', + stock: old ? old.stock : '' + } + }) + } else if (valueArr.length === 2) { + result = cartesianProduct(valueArr).map(([v1, v2]) => { + const old = skuTable.value.find(row => row.spec0 === v1 && row.spec1 === v2) + return { + spec0: v1, + spec1: v2, + price: old ? old.price : '', + cost_price: old ? old.cost_price : '', + stock: old ? old.stock : '' + } + }) + } else if (valueArr.length === 3) { + result = cartesianProduct(valueArr).map(([v1, v2, v3]) => { + const old = skuTable.value.find(row => row.spec0 === v1 && row.spec1 === v2 && row.spec2 === v3) + return { + spec0: v1, + spec1: v2, + spec2: v3, + price: old ? old.price : '', + cost_price: old ? old.cost_price : '', + stock: old ? old.stock : '' + } + }) + } + + // 合并已有 skuTable 的价格等数据 + result.forEach(row => { + // 1. 精确匹配 + let old = skuTable.value.find(oldRow => + ['spec0', 'spec1', 'spec2'].every(key => row[key] === oldRow[key]) + ) + // 2. 如果精确找不到,尝试模糊匹配(只要有一项不同也算) + if (!old) { + old = skuTable.value.find(oldRow => { + let sameCount = 0 + let total = 0 + for (const key of ['spec0', 'spec1', 'spec2']) { + if (row[key] !== undefined && oldRow[key] !== undefined) { + total++ + if (row[key] === oldRow[key]) sameCount++ + } + } + // 至少有一项相同(比如只改了一个规格值) + return total > 0 && sameCount === total - 1 + }) + } + if (old) { + row.price = old.price + row.cost_price = old.cost_price + row.stock = old.stock + } + }) + + skuTable.value = result +} + +// 监听规格变化自动生成表格 +watch(specs, updateSkuTable, { deep: true }) defineExpose({ open, setFormData, getDetail, selectFirstCategory, - selectSecondCategory + selectSecondCategory, + mode: 'default', // 或 'simple' }) diff --git a/src/views/goods/edit.vue b/src/views/goods/edit.vue index 0d33460..73fc536 100644 --- a/src/views/goods/edit.vue +++ b/src/views/goods/edit.vue @@ -15,26 +15,26 @@
-
+
- +
-
+
- +
- +
@@ -47,7 +47,6 @@ - @@ -79,9 +78,115 @@
-
+ +
+
+ +
删除 +
+
属性名称:
+ +
+
+
属性值:
+
+ + + + +
+ +
+ + + + + +
+
+
+ + 增加字段 + + +
+
+ 增加销售属性 +
+ + {{ stockKeepUnits }} + + + + + + + + + + + + + + + + + + + + + + + + +
- - + + + + + + + + + + + + + + 显示 + 不显示 + + + + + + + + + + + + + + 赠送固定积分 + + + + 按比例赠送积分 + + % + + + + + + + + 立即上架 + 放入仓库 + + - - - -
@@ -138,8 +297,9 @@ import type { FormInstance } from 'element-plus' import Popup from '@/components/popup/index.vue' import { apiGoodsAdd, apiGoodsEdit, apiGoodsDetail, checkCategory } from '@/api/goods' import type { PropType } from 'vue' -import { Editor, Toolbar } from '@wangeditor/editor-for-vue' -import '@wangeditor/editor/dist/css/style.css' // 引入 css +import feedback from '@/utils/feedback' +import { ref, watch, type Ref, computed } from 'vue' +import sku from './components/sku.vue' defineProps({ dictData: { @@ -170,56 +330,58 @@ const formData = reactive({ remark: '', image: '', goods_image: '', + video: '', + poster: '', one_price: '', one_cost_price: '', one_stock: '', + one_market_price: '', + one_spec_image: '', + one_volume: '', + one_weight: '', + one_bar_code: '', spec_name: '', - spec_type: 1, + spec_type: 2, content: '', - + sales_sum: 0, + click_count: 0, + virtual_sales_sum: '', + virtual_click: '', + stock_warn: 0, + is_show_stock: 1, + is_express: 1, + is_integral: 0, + give_integral_type: 0, + give_integral_num: '', + give_integral_ratio: '', + status: 0, brand_id: '', supplier_id: '', - status: '', - - video: '', - one_spec_image: '', - poster: '', sort: '', - sales_sum: '', - virtual_sales_sum: '', - click_count: '', - virtual_click: '', - max_price: '', - min_price: '', - market_price: '', - stock: '', - stock_warn: '', - is_show_stock: '', - free_shipping_type: '', - free_shipping: '', - free_shipping_template_id: '', - is_commission: '', - first_ratio: '', - second_ratio: '', - three_ratio: '', - is_share_bouns: '', - region_ratio: '', - shareholder_ratio: '', + max_price: 0, + min_price: 0, + market_price: 0, + free_shipping_type: 0, + free_shipping: 0, + free_shipping_template_id: 0, + is_commission: 0, + first_ratio: 0, + second_ratio: 0, + three_ratio: 0, + is_share_bouns: 0, + region_ratio: 0, + shareholder_ratio: 0, is_new: '', is_best: '', is_like: '', - is_team: '', - is_integral: '', - is_member: '', - give_integral_type: '', - give_integral: '', - del: '', - is_express: '', + is_team: 0, + is_member: 0, + give_integral: 0, is_selffetch: '', }) -// 表单验证 +// // 表单验证 const formRules = reactive({ name: [{ required: true, @@ -231,61 +393,186 @@ const formRules = reactive({ message: '请选择商品分类', trigger: ['blur'] }], - // status: [{ - // required: true, - // message: '请输入商品状态:-1-回收站;0-下架;1-上架', - // trigger: ['blur'] - // }], - // image: [{ - // required: true, - // message: '请输入商品主图', - // trigger: ['blur'] - // }], - // is_show_stock: [{ - // required: true, - // message: '请输入是否显示库存:1-是;0-否', - // trigger: ['blur'] - // }], - // free_shipping_type: [{ - // required: true, - // message: '请输入运费类型:1-包邮;2-统一运费;3-运费模板', - // trigger: ['blur'] - // }], - // is_commission: [{ - // required: true, - // message: '请输入分销佣金:1-开启;0-不开启', - // trigger: ['blur'] - // }], - // is_share_bouns: [{ - // required: true, - // message: '请输入区域股东分红:1-开启;0-不开启', - // trigger: ['blur'] - // }], - // is_team: [{ - // required: true, - // message: '请输入是否开启拼团[0=否, 1=是]', - // trigger: ['blur'] - // }], - // is_integral: [{ - // required: true, - // message: '请输入积分抵扣:1-开启;0-不开启', - // trigger: ['blur'] - // }], - // is_member: [{ - // required: true, - // message: '请输入会员价:1-开启;0-不开启', - // trigger: ['blur'] - // }], - // give_integral_type: [{ - // required: true, - // message: '请输入赠送积分类型:0-不赠送;1-赠送固定积分;2-按比例赠送积分', - // trigger: ['blur'] - // }] + image: [{ + required: true, + message: '请上传商品主图', + trigger: ['blur'] + }], + goods_image: [{ + required: true, + message: '请上传商品轮播图', + trigger: ['blur'] + }], }) -onMounted(() => { - category() -}); +/*******************************多规格开始**************************************/ +import type { skuType, skuAttrItemType } from './components/type.d' +import { deepClone } from '@yipai-front-end/lib' + +const skuAttributes: Ref = ref([]) +const stockKeepUnits: Ref = ref([]) +let afterSku: skuType[] = [] + + +const skuAttrItem: skuAttrItemType = { + title: '', + isAddImage: false, + values: [{ thumbnailUrl: '', attributeValue: '' }], +} + +// 监听sku本身的变化,并将当前sku进行备份 +watch( + () => stockKeepUnits.value, + (value) => { + console.log("value>>>", value); + afterSku = deepClone(value) + }, + { deep: true } +) + +// 监听销售属性的变化,并构建sku +watch( + () => skuAttributes.value, + (value) => { + if (value.length) { + console.log("123>>>", 123); + generateSku(deepClone(value)) + } + }, + { deep: true } +) + +/** + * 更新销售属性构建sku + * @param skuAttribute + */ +function generateSku(skuAttribute: skuAttrItemType[]) { + let attrValue: any[] = [] + skuAttribute.map((item) => { + attrValue.push(item.values) + }) + let skus: any[] = [] + if (attrValue.length === 0) { + stockKeepUnits.value = [] + return + } + + skus = attrValue.reduce((col: any[], set) => { + let res: any[] = [] + col.forEach((c) => { + set.forEach((s) => { + let t = c.attributeValue + ',' + s.attributeValue + res.push({ attributeValue: t, thumbnailUrl: c.thumbnailUrl || s.thumbnailUrl || '' }) + }) + }) + return res + }) + // 增加,回显相关字段 + skus.map((e: skuType) => { + // 寻找销售规格一致的副本数据 + let old = afterSku.find((item) => item.attributeValue == e.attributeValue) + console.log('old=', old) + + console.log('单项', e) + e.id = old == null ? '' : old.id + e.price = old == null ? '' : old.price + e.marketPrice = old == null ? '' : old.marketPrice + e.stock = old == null ? '' : old.stock + e.specificationBarCode = old == null ? '' : old.specificationBarCode + return e + }) + console.log(skus, 'skus') + stockKeepUnits.value = skus +} + +/** + * 删除销售属性 + * @param index + */ +function deleteSkuAttr(index) { + skuAttributes.value.splice(index, 1) +} + +/** + * 删除销售属性字段 + * @param index + * @param cindex + */ +function deleteSkuAttrName(index: number, cindex: number) { + skuAttributes.value[index].values.splice(cindex, 1) +} + +/** + * 增加sku属性图片 + * @param index + * @param cindex + */ +async function addSkuAttrImage(index: number, cindex: number) { + let res = await chooseToFile() + console.log(res) + // 生产环境此处应该是上传到服务端,获取线上url + // 此处写法仅限测试,上传大图片可能造成卡顿 + _blobToDataUrl(res[0], (res) => { + skuAttributes.value[index].values[cindex].thumbnailUrl = res + }) +} + +/** + * 增加销售属性字段 + * @param index + */ +function addSkuAttrName(index: number) { + skuAttributes.value[index].values.push({ attributeValue: '', thumbnailUrl: '' }) +} + +/** + * 切换首个sku是否上传图片的状态 + */ +function toggleSkuImg(index) { + let { isAddImage } = skuAttributes.value[index] + if (isAddImage) { + skuAttributes.value[index].values.map((e) => { + e.thumbnailUrl = '' + return e + }) + } + skuAttributes.value[index].isAddImage = !isAddImage +} + +/** + * 增加销售属性 + */ +function addSkuAttr() { + skuAttributes.value.push(deepClone(skuAttrItem)) +} + +/** + * 动态更新表格字段 + * @param index + * @param dataIndex + */ +function changeSkuData(index: number, dataIndex: string, evt: any) { + let value = evt.target.value + stockKeepUnits.value[index][dataIndex] = value +} + +function save() { + // message.success('请查看控制台输出') + console.log('销售属性:', skuAttributes.value) + console.log('sku:', stockKeepUnits.value) +} + +function _blobToDataUrl(file, callback) { + const reader = new FileReader() + reader.onload = () => { + const url = URL.createObjectURL(file) // 获取临时访问链接 + callback(url) + } + reader.readAsDataURL(file) +} + + +/*******************************多规格结束**************************************/ // 获取详情 const setFormData = async (data: Record) => { @@ -301,15 +588,106 @@ const getDetail = async (row: Record) => { const data = await apiGoodsDetail({ id: row.id }) - setFormData(data) + + // 提取 abs_image 并重新赋值给 goods_image + if (Array.isArray(data['base']['goods_image'])) { + data['base']['goods_image'] = data['base']['goods_image'].map(item => item.abs_image) + } + + setFormData(data['base']) + // 编辑--重新组成数据 + const { base, item, spec } = data + if (base.spec_type == 1) { + // 单规格 + formData['one_price'] = item[0].price + formData['one_cost_price'] = item[0].cost_price + formData['one_stock'] = item[0].stock + formData['one_volume'] = item[0].volume + formData['one_weight'] = item[0].weight + formData['one_bar_code'] = item[0].bar_code + } else if (base.spec_type == 2) { + console.log("456>>>", 456); + // 多规格 + skuAttributes.value = [ + { + isAddImage: false, + title: '口味', + values: [ + { + attributeValue: '甜', + thumbnailUrl: '' + }, + { + attributeValue: '辣', + thumbnailUrl: '' + } + ] + }, + { + isAddImage: false, + title: '尺寸', + values: [ + { + attributeValue: '大', + thumbnailUrl: '' + }, + ] + } + ] + + stockKeepUnits.value = [ + { + attributeValue: '甜,大', + id: '', + marketPrice: "2", + price: "1", + specificationBarCode: '', + stock: '3', + thumbnailUrl: '' + }, + { + attributeValue: '辣,大', + id: '', + marketPrice: "2", + price: "1", + specificationBarCode: '', + stock: '3', + thumbnailUrl: '' + } + ] + afterSku = [ + { + attributeValue: '甜,大', + id: '', + marketPrice: "2", + price: "1", + specificationBarCode: '', + stock: '3', + thumbnailUrl: '' + }, + { + attributeValue: '辣,大', + id: '', + marketPrice: "2", + price: "1", + specificationBarCode: '', + stock: '3', + thumbnailUrl: '' + } + ] + // generateSku(deepClone(skuAttributes.value)) + } + + // 设置分类 + selectFirstCategory(data.first_category_id) + selectSecondCategory(data.second_category_id) } -// -// 创建分类列表 +// let goodsCategory = reactive([]) let firstCategory = reactive([]) let secondCategory = reactive([]) let thirdCategory = reactive([]) -const category = async () => { +const getCategory = async () => { const res = await checkCategory() goodsCategory = res firstCategory = res.filter((item: { pid: number }) => { @@ -348,26 +726,29 @@ const selectSecondCategory = (id: number) => { // 提交按钮 const handleSubmit = async () => { + console.log('销售属性:', skuAttributes.value) + console.log('sku:', stockKeepUnits.value) + return false // await formRef.value?.validate() - const data = { ...formData, } - data.price = skuTable.value.map(item => item.price) - data.cost_price = skuTable.value.map(item => item.cost_price) - data.stock = skuTable.value.map(item => item.stock) - data.spec_value_str = skuTable.value.map(item => { - const arr = [] - if ('spec0' in item) arr.push(item.spec0) - if ('spec1' in item) arr.push(item.spec1) - if ('spec2' in item) arr.push(item.spec2) - return arr.join(',') - }) - // data.spec_name = specs.value.map(item => item.name); - // data.spec_values = specs.value.map(item => item.valuesStr.replace(/\n/g, ',')); - console.log("data>>>", data); + const data = { ...formData } + if (data.spec_type == 1 && !data.one_price && !data.one_cost_price && !data.one_stock) { + feedback.notifyError('请填写商品规格') + return false + } + console.log("attribute>>>", attribute); + data.spec_name = attribute.spec.map(item => item.name) // 规格名称 + data.spec_values = attribute.spec.map(item => item.item.map(i => i.name).join(',')) // 规格项名称 + data.spec_value_str = attribute.sku.map(item => item.sku) // 规格项笛卡尔积 + data.price = attribute.sku.map(item => item.price) + data.cost_price = attribute.sku.map(item => item.cost_price) + data.stock = attribute.sku.map(item => item.stock) + + if (data.spec_type == 2 && !data.price && !data.cost_price && !data.stock && !data.spec_value_str) { + feedback.notifyError('请完善商品多规格字段') + return false + } - // console.log("specs>>>", specs); - // console.log("specs>>>", skuTable); - return false; mode.value == 'edit' ? await apiGoodsEdit(data) : await apiGoodsAdd(data) @@ -386,151 +767,112 @@ const handleClose = () => { emit('close') } -// 多规格相关 -// const specs = ref([ -// { name: '', valuesStr: '', values: [] } -// ]) -const specs = ref([]) -const skuTable = ref([]) -// 添加规格项(最多3个) -const addSpec = () => { - if (specs.value.length < 3) { - specs.value.push({ name: '', valuesStr: '', values: [] }) - syncSpecToFormData() - } -} +onMounted(() => { + getCategory() -// 删除规格项 -const removeSpec = (idx: number) => { - specs.value.splice(idx, 1) - updateSkuTable() - syncSpecToFormData() -} + // console.log('销售属性:', skuAttributes.value) + // console.log('sku:', stockKeepUnits.value) -// 规格值变更 -const onSpecValueChange = (idx: number) => { - const spec = specs.value[idx] - spec.values = spec.valuesStr - .split('\n') - .map(v => v.trim()) - .filter(v => v) - updateSkuTable() - syncSpecToFormData() -} + // skuAttributes.value = [ + // { + // isAddImage: false, + // title: '口味', + // values: [ + // { + // attributeValue: '甜', + // thumbnailUrl: '' + // }, + // { + // attributeValue: '辣', + // thumbnailUrl: '' + // } + // ] + // }, + // { + // isAddImage: false, + // title: '尺寸', + // values: [ + // { + // attributeValue: '大', + // thumbnailUrl: '' + // }, + // ] + // } + // ] -// 计算所有规格组合(笛卡尔积) -function cartesianProduct(arr: any[][]) { - if (arr.length === 0) return [] - return arr.reduce((a, b) => - a.flatMap(d => b.map(e => [].concat(d, e))) - ) -} - -// 同步规格数据到formData -const syncSpecToFormData = () => { - formData.spec_name = specs.value.map(item => item.name) - formData.spec_values = specs.value.map(item => item.valuesStr.replace(/\n/g, ',')) -} + // stockKeepUnits.value = [ + // { + // attributeValue: '甜,大', + // id: '', + // marketPrice: "2", + // price: "1", + // specificationBarCode: '', + // stock: '3', + // thumbnailUrl: '' + // }, + // { + // attributeValue: '辣,大', + // id: '', + // marketPrice: "2", + // price: "1", + // specificationBarCode: '', + // stock: '3', + // thumbnailUrl: '' + // } + // ] + // generateSku(deepClone(skuAttributes.value)) + // skuAttributes = { + // 0: { + // 'isAddImage': false, + // 'title': '口味', + // 'values': { + // { + // 'attributeValue': '甜', + // 'thumbnailUrl': '' + // }, + // { + // 'attributeValue': '辣', + // 'thumbnailUrl': '' + // } + // } + // }, + // 1: { + // 'isAddImage': false, + // 'title': '尺寸', + // 'values': { + // { + // 'attributeValue': '大', + // 'thumbnailUrl': '' + // } + // } + // } + // } -// 更新SKU表格 -const updateSkuTable = () => { - const valueArr = specs.value.map(s => s.values) - if (valueArr.some(arr => arr.length === 0)) { - skuTable.value = [] - return - } - let result: any[] = [] - if (valueArr.length === 1) { - result = valueArr[0].map(v1 => ({ - spec0: v1, - price: '', - cost_price: '', - stock: '' - })) - } else if (valueArr.length === 2) { - result = cartesianProduct(valueArr).map(([v1, v2]) => ({ - spec0: v1, - spec1: v2, - price: '', - cost_price: '', - stock: '' - })) - } else if (valueArr.length === 3) { - result = cartesianProduct(valueArr).map(([v1, v2, v3]) => ({ - spec0: v1, - spec1: v2, - spec2: v3, - price: '', - cost_price: '', - stock: '' - })) - } - skuTable.value = result -} +}); -// 编辑器 -// 编辑器实例,必须用 shallowRef -const editorRef = shallowRef() -const toolbarConfig = {} -const materialPickerRef = ref() -const showImagePicker = ref(false) -let imageInsertFn: ((url: string) => void) | null = null - -const editorConfig = { - placeholder: '请输入内容...', - MENU_CONF: { - uploadImage: { - // 自定义上传图片 - customBrowseAndUpload(insertFn: (url: string, alt: string, href: string) => void) { - showImagePicker.value = true - imageInsertFn = (url: string) => { - insertFn(url, '', '') - // 直接调用 material-picker 的打开方法 - materialPickerRef.value?.open(-1) - } - } - } - } -} - -// 图片选择后回调 -function onImageSelected(val: string | string[]) { - const urls = Array.isArray(val) ? val : [val] - if (imageInsertFn && urls.length) { - urls.forEach(url => { - imageInsertFn!(url) - }) - imageInsertFn = null - } - showImagePicker.value = false -} - -// 组件销毁时,也及时销毁编辑器 -onBeforeUnmount(() => { - const editor = editorRef.value - if (editor == null) return - editor.destroy() -}) - -const handleCreated = (editor) => { - editorRef.value = editor // 记录 editor 实例,重要! -} - -// 监听规格变化自动生成表格 -watch(specs, updateSkuTable, { deep: true }) defineExpose({ open, setFormData, getDetail, selectFirstCategory, selectSecondCategory, - editorRef, mode: 'default', // 或 'simple' - toolbarConfig, - editorConfig, - handleCreated, }) + + diff --git a/src/views/goods/goods.vue b/src/views/goods/goods.vue new file mode 100644 index 0000000..3fcf850 --- /dev/null +++ b/src/views/goods/goods.vue @@ -0,0 +1,178 @@ + + + + \ No newline at end of file diff --git a/src/views/goods/index.vue b/src/views/goods/index.vue index 597e5c4..a7f0960 100644 --- a/src/views/goods/index.vue +++ b/src/views/goods/index.vue @@ -106,7 +106,7 @@ const handleEdit = async (data: any) => { showEdit.value = true await nextTick() editRef.value?.open('edit') - editRef.value?.setFormData(data) + editRef.value?.getDetail(data) } // 删除 diff --git a/src/views/goods/power-set.js b/src/views/goods/power-set.js new file mode 100644 index 0000000..e6dd87c --- /dev/null +++ b/src/views/goods/power-set.js @@ -0,0 +1,28 @@ +export default function bwPowerSet(originalSet) { + const subSets = [] + + // We will have 2^n possible combinations (where n is a length of original set). + // It is because for every element of original set we will decide whether to include + // it or not (2 options for each set element). + const numberOfCombinations = 2 ** originalSet.length + + // Each number in binary representation in a range from 0 to 2^n does exactly what we need: + // it shows by its bits (0 or 1) whether to include related element from the set or not. + // For example, for the set {1, 2, 3} the binary number of 0b010 would mean that we need to + // include only "2" to the current set. + for (let combinationIndex = 0; combinationIndex < numberOfCombinations; combinationIndex += 1) { + const subSet = [] + + for (let setElementIndex = 0; setElementIndex < originalSet.length; setElementIndex += 1) { + // Decide whether we need to include current element into the subset or not. + if (combinationIndex & (1 << setElementIndex)) { + subSet.push(originalSet[setElementIndex]) + } + } + + // Add current subset to the list of all subsets. + subSets.push(subSet) + } + + return subSets +} \ No newline at end of file diff --git a/src/views/goods/sku.vue b/src/views/goods/sku.vue new file mode 100644 index 0000000..98ee3df --- /dev/null +++ b/src/views/goods/sku.vue @@ -0,0 +1,240 @@ + + +