新增富文本,删除了一大堆之前的

This commit is contained in:
yuanteng 2025-03-16 23:22:08 +08:00
parent 0d16961cd6
commit 4ee1cfa3a9
33 changed files with 2121 additions and 587 deletions

View File

@ -11,3 +11,11 @@
<script type="module" src="/src/main.ts"></script>
</body>
</html>
<style>
html,body,#app{
height:100%;
margin: 0px;
padding: 0px;
}
</style>

278
package-lock.json generated
View File

@ -913,6 +913,7 @@
"version": "3.5.12",
"resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.12.tgz",
"integrity": "sha512-ISyBTRMmMYagUxhcpyEH0hpXRd/KqDU4ymofPgl2XAkY9ZhQ+h0ovEZJIiPop13UmR/54oA2cgMDjgroRelaEw==",
"dev": true,
"requires": {
"@babel/parser": "^7.25.3",
"@vue/shared": "3.5.12",
@ -925,34 +926,98 @@
"version": "3.5.12",
"resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.12.tgz",
"integrity": "sha512-9G6PbJ03uwxLHKQ3P42cMTi85lDRvGLB2rSGOiQqtXELat6uI4n8cNz9yjfVHRPIu+MsK6TE418Giruvgptckg==",
"dev": true,
"requires": {
"@vue/compiler-core": "3.5.12",
"@vue/shared": "3.5.12"
}
},
"@vue/compiler-sfc": {
"version": "3.5.12",
"resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.12.tgz",
"integrity": "sha512-2k973OGo2JuAa5+ZlekuQJtitI5CgLMOwgl94BzMCsKZCX/xiqzJYzapl4opFogKHqwJk34vfsaKpfEhd1k5nw==",
"version": "3.2.31",
"resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.2.31.tgz",
"integrity": "sha512-748adc9msSPGzXgibHiO6T7RWgfnDcVQD+VVwYgSsyyY8Ans64tALHZANrKtOzvkwznV/F4H7OAod/jIlp/dkQ==",
"requires": {
"@babel/parser": "^7.25.3",
"@vue/compiler-core": "3.5.12",
"@vue/compiler-dom": "3.5.12",
"@vue/compiler-ssr": "3.5.12",
"@vue/shared": "3.5.12",
"@babel/parser": "^7.16.4",
"@vue/compiler-core": "3.2.31",
"@vue/compiler-dom": "3.2.31",
"@vue/compiler-ssr": "3.2.31",
"@vue/reactivity-transform": "3.2.31",
"@vue/shared": "3.2.31",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.11",
"postcss": "^8.4.47",
"source-map-js": "^1.2.0"
"magic-string": "^0.25.7",
"postcss": "^8.1.10",
"source-map": "^0.6.1"
},
"dependencies": {
"@vue/compiler-core": {
"version": "3.2.31",
"resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.2.31.tgz",
"integrity": "sha512-aKno00qoA4o+V/kR6i/pE+aP+esng5siNAVQ422TkBNM6qA4veXiZbSe8OTXHXquEi/f6Akc+nLfB4JGfe4/WQ==",
"requires": {
"@babel/parser": "^7.16.4",
"@vue/shared": "3.2.31",
"estree-walker": "^2.0.2",
"source-map": "^0.6.1"
}
},
"@vue/compiler-dom": {
"version": "3.2.31",
"resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.2.31.tgz",
"integrity": "sha512-60zIlFfzIDf3u91cqfqy9KhCKIJgPeqxgveH2L+87RcGU/alT6BRrk5JtUso0OibH3O7NXuNOQ0cDc9beT0wrg==",
"requires": {
"@vue/compiler-core": "3.2.31",
"@vue/shared": "3.2.31"
}
},
"@vue/shared": {
"version": "3.2.31",
"resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.31.tgz",
"integrity": "sha512-ymN2pj6zEjiKJZbrf98UM2pfDd6F2H7ksKw7NDt/ZZ1fh5Ei39X5tABugtT03ZRlWd9imccoK0hE8hpjpU7irQ=="
},
"magic-string": {
"version": "0.25.9",
"resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.25.9.tgz",
"integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
"requires": {
"sourcemap-codec": "^1.4.8"
}
}
}
},
"@vue/compiler-ssr": {
"version": "3.5.12",
"resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.12.tgz",
"integrity": "sha512-eLwc7v6bfGBSM7wZOGPmRavSWzNFF6+PdRhE+VFJhNCgHiF8AM7ccoqcv5kBXA2eWUfigD7byekvf/JsOfKvPA==",
"version": "3.2.31",
"resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.2.31.tgz",
"integrity": "sha512-mjN0rqig+A8TVDnsGPYJM5dpbjlXeHUm2oZHZwGyMYiGT/F4fhJf/cXy8QpjnLQK4Y9Et4GWzHn9PS8AHUnSkw==",
"requires": {
"@vue/compiler-dom": "3.5.12",
"@vue/shared": "3.5.12"
"@vue/compiler-dom": "3.2.31",
"@vue/shared": "3.2.31"
},
"dependencies": {
"@vue/compiler-core": {
"version": "3.2.31",
"resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.2.31.tgz",
"integrity": "sha512-aKno00qoA4o+V/kR6i/pE+aP+esng5siNAVQ422TkBNM6qA4veXiZbSe8OTXHXquEi/f6Akc+nLfB4JGfe4/WQ==",
"requires": {
"@babel/parser": "^7.16.4",
"@vue/shared": "3.2.31",
"estree-walker": "^2.0.2",
"source-map": "^0.6.1"
}
},
"@vue/compiler-dom": {
"version": "3.2.31",
"resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.2.31.tgz",
"integrity": "sha512-60zIlFfzIDf3u91cqfqy9KhCKIJgPeqxgveH2L+87RcGU/alT6BRrk5JtUso0OibH3O7NXuNOQ0cDc9beT0wrg==",
"requires": {
"@vue/compiler-core": "3.2.31",
"@vue/shared": "3.2.31"
}
},
"@vue/shared": {
"version": "3.2.31",
"resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.31.tgz",
"integrity": "sha512-ymN2pj6zEjiKJZbrf98UM2pfDd6F2H7ksKw7NDt/ZZ1fh5Ei39X5tABugtT03ZRlWd9imccoK0hE8hpjpU7irQ=="
}
}
},
"@vue/compiler-vue2": {
@ -987,46 +1052,112 @@
}
},
"@vue/reactivity": {
"version": "3.5.12",
"resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.12.tgz",
"integrity": "sha512-UzaN3Da7xnJXdz4Okb/BGbAaomRHc3RdoWqTzlvd9+WBR5m3J39J1fGcHes7U3za0ruYn/iYy/a1euhMEHvTAg==",
"version": "3.2.31",
"resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.2.31.tgz",
"integrity": "sha512-HVr0l211gbhpEKYr2hYe7hRsV91uIVGFYNHj73njbARVGHQvIojkImKMaZNDdoDZOIkMsBc9a1sMqR+WZwfSCw==",
"requires": {
"@vue/shared": "3.5.12"
"@vue/shared": "3.2.31"
},
"dependencies": {
"@vue/shared": {
"version": "3.2.31",
"resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.31.tgz",
"integrity": "sha512-ymN2pj6zEjiKJZbrf98UM2pfDd6F2H7ksKw7NDt/ZZ1fh5Ei39X5tABugtT03ZRlWd9imccoK0hE8hpjpU7irQ=="
}
}
},
"@vue/reactivity-transform": {
"version": "3.2.31",
"resolved": "https://registry.npmmirror.com/@vue/reactivity-transform/-/reactivity-transform-3.2.31.tgz",
"integrity": "sha512-uS4l4z/W7wXdI+Va5pgVxBJ345wyGFKvpPYtdSgvfJfX/x2Ymm6ophQlXXB6acqGHtXuBqNyyO3zVp9b1r0MOA==",
"requires": {
"@babel/parser": "^7.16.4",
"@vue/compiler-core": "3.2.31",
"@vue/shared": "3.2.31",
"estree-walker": "^2.0.2",
"magic-string": "^0.25.7"
},
"dependencies": {
"@vue/compiler-core": {
"version": "3.2.31",
"resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.2.31.tgz",
"integrity": "sha512-aKno00qoA4o+V/kR6i/pE+aP+esng5siNAVQ422TkBNM6qA4veXiZbSe8OTXHXquEi/f6Akc+nLfB4JGfe4/WQ==",
"requires": {
"@babel/parser": "^7.16.4",
"@vue/shared": "3.2.31",
"estree-walker": "^2.0.2",
"source-map": "^0.6.1"
}
},
"@vue/shared": {
"version": "3.2.31",
"resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.31.tgz",
"integrity": "sha512-ymN2pj6zEjiKJZbrf98UM2pfDd6F2H7ksKw7NDt/ZZ1fh5Ei39X5tABugtT03ZRlWd9imccoK0hE8hpjpU7irQ=="
},
"magic-string": {
"version": "0.25.9",
"resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.25.9.tgz",
"integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
"requires": {
"sourcemap-codec": "^1.4.8"
}
}
}
},
"@vue/runtime-core": {
"version": "3.5.12",
"resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.12.tgz",
"integrity": "sha512-hrMUYV6tpocr3TL3Ad8DqxOdpDe4zuQY4HPY3X/VRh+L2myQO8MFXPAMarIOSGNu0bFAjh1yBkMPXZBqCk62Uw==",
"version": "3.2.31",
"resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.2.31.tgz",
"integrity": "sha512-Kcog5XmSY7VHFEMuk4+Gap8gUssYMZ2+w+cmGI6OpZWYOEIcbE0TPzzPHi+8XTzAgx1w/ZxDFcXhZeXN5eKWsA==",
"requires": {
"@vue/reactivity": "3.5.12",
"@vue/shared": "3.5.12"
"@vue/reactivity": "3.2.31",
"@vue/shared": "3.2.31"
},
"dependencies": {
"@vue/shared": {
"version": "3.2.31",
"resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.31.tgz",
"integrity": "sha512-ymN2pj6zEjiKJZbrf98UM2pfDd6F2H7ksKw7NDt/ZZ1fh5Ei39X5tABugtT03ZRlWd9imccoK0hE8hpjpU7irQ=="
}
}
},
"@vue/runtime-dom": {
"version": "3.5.12",
"resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.12.tgz",
"integrity": "sha512-q8VFxR9A2MRfBr6/55Q3umyoN7ya836FzRXajPB6/Vvuv0zOPL+qltd9rIMzG/DbRLAIlREmnLsplEF/kotXKA==",
"version": "3.2.31",
"resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.2.31.tgz",
"integrity": "sha512-N+o0sICVLScUjfLG7u9u5XCjvmsexAiPt17GNnaWHJUfsKed5e85/A3SWgKxzlxx2SW/Hw7RQxzxbXez9PtY3g==",
"requires": {
"@vue/reactivity": "3.5.12",
"@vue/runtime-core": "3.5.12",
"@vue/shared": "3.5.12",
"csstype": "^3.1.3"
"@vue/runtime-core": "3.2.31",
"@vue/shared": "3.2.31",
"csstype": "^2.6.8"
},
"dependencies": {
"@vue/shared": {
"version": "3.2.31",
"resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.31.tgz",
"integrity": "sha512-ymN2pj6zEjiKJZbrf98UM2pfDd6F2H7ksKw7NDt/ZZ1fh5Ei39X5tABugtT03ZRlWd9imccoK0hE8hpjpU7irQ=="
}
}
},
"@vue/server-renderer": {
"version": "3.5.12",
"resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.12.tgz",
"integrity": "sha512-I3QoeDDeEPZm8yR28JtY+rk880Oqmj43hreIBVTicisFTx/Dl7JpG72g/X7YF8hnQD3IFhkky5i2bPonwrTVPg==",
"version": "3.2.31",
"resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.2.31.tgz",
"integrity": "sha512-8CN3Zj2HyR2LQQBHZ61HexF5NReqngLT3oahyiVRfSSvak+oAvVmu8iNLSu6XR77Ili2AOpnAt1y8ywjjqtmkg==",
"requires": {
"@vue/compiler-ssr": "3.5.12",
"@vue/shared": "3.5.12"
"@vue/compiler-ssr": "3.2.31",
"@vue/shared": "3.2.31"
},
"dependencies": {
"@vue/shared": {
"version": "3.2.31",
"resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.31.tgz",
"integrity": "sha512-ymN2pj6zEjiKJZbrf98UM2pfDd6F2H7ksKw7NDt/ZZ1fh5Ei39X5tABugtT03ZRlWd9imccoK0hE8hpjpU7irQ=="
}
}
},
"@vue/shared": {
"version": "3.5.12",
"resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.12.tgz",
"integrity": "sha512-L2RPSAwUFbgZH20etwrXyVyCBu9OxRSi8T/38QsvnkJyvq2LufW2lDCOzm7t/U9C1mkhJGWYfCuFBCmIuNivrg=="
"integrity": "sha512-L2RPSAwUFbgZH20etwrXyVyCBu9OxRSi8T/38QsvnkJyvq2LufW2lDCOzm7t/U9C1mkhJGWYfCuFBCmIuNivrg==",
"dev": true
},
"@vue/tsconfig": {
"version": "0.5.1",
@ -1359,9 +1490,9 @@
}
},
"csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
"version": "2.6.21",
"resolved": "https://registry.npmmirror.com/csstype/-/csstype-2.6.21.tgz",
"integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w=="
},
"d": {
"version": "1.0.2",
@ -1460,7 +1591,8 @@
"entities": {
"version": "4.5.0",
"resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz",
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
"dev": true
},
"errno": {
"version": "0.1.8",
@ -2309,12 +2441,12 @@
"optional": true
},
"pinia": {
"version": "2.2.4",
"resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.2.4.tgz",
"integrity": "sha512-K7ZhpMY9iJ9ShTC0cR2+PnxdQRuwVIsXDO/WIEV/RnMC/vmSoKDTKW/exNQYPI+4ij10UjXqdNiEHwn47McANQ==",
"version": "2.0.35",
"resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.0.35.tgz",
"integrity": "sha512-P1IKKQWhxGXiiZ3atOaNI75bYlFUbRxtJdhPLX059Z7+b9Z04rnTZdSY8Aph1LA+/4QEMAYHsTQ638Wfe+6K5g==",
"requires": {
"@vue/devtools-api": "^6.6.3",
"vue-demi": "^0.14.10"
"@vue/devtools-api": "^6.5.0",
"vue-demi": "*"
}
},
"pinia-plugin-persistedstate": {
@ -2548,15 +2680,18 @@
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"optional": true
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
},
"source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="
},
"sourcemap-codec": {
"version": "1.4.8",
"resolved": "https://registry.npmmirror.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA=="
},
"ssr-window": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/ssr-window/-/ssr-window-3.0.0.tgz",
@ -2760,15 +2895,42 @@
"dev": true
},
"vue": {
"version": "3.5.12",
"resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.12.tgz",
"integrity": "sha512-CLVZtXtn2ItBIi/zHZ0Sg1Xkb7+PU32bJJ8Bmy7ts3jxXTcbfsEfBivFYYWz1Hur+lalqGAh65Coin0r+HRUfg==",
"version": "3.2.31",
"resolved": "https://registry.npmmirror.com/vue/-/vue-3.2.31.tgz",
"integrity": "sha512-odT3W2tcffTiQCy57nOT93INw1auq5lYLLYtWpPYQQYQOOdHiqFct9Xhna6GJ+pJQaF67yZABraH47oywkJgFw==",
"requires": {
"@vue/compiler-dom": "3.5.12",
"@vue/compiler-sfc": "3.5.12",
"@vue/runtime-dom": "3.5.12",
"@vue/server-renderer": "3.5.12",
"@vue/shared": "3.5.12"
"@vue/compiler-dom": "3.2.31",
"@vue/compiler-sfc": "3.2.31",
"@vue/runtime-dom": "3.2.31",
"@vue/server-renderer": "3.2.31",
"@vue/shared": "3.2.31"
},
"dependencies": {
"@vue/compiler-core": {
"version": "3.2.31",
"resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.2.31.tgz",
"integrity": "sha512-aKno00qoA4o+V/kR6i/pE+aP+esng5siNAVQ422TkBNM6qA4veXiZbSe8OTXHXquEi/f6Akc+nLfB4JGfe4/WQ==",
"requires": {
"@babel/parser": "^7.16.4",
"@vue/shared": "3.2.31",
"estree-walker": "^2.0.2",
"source-map": "^0.6.1"
}
},
"@vue/compiler-dom": {
"version": "3.2.31",
"resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.2.31.tgz",
"integrity": "sha512-60zIlFfzIDf3u91cqfqy9KhCKIJgPeqxgveH2L+87RcGU/alT6BRrk5JtUso0OibH3O7NXuNOQ0cDc9beT0wrg==",
"requires": {
"@vue/compiler-core": "3.2.31",
"@vue/shared": "3.2.31"
}
},
"@vue/shared": {
"version": "3.2.31",
"resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.31.tgz",
"integrity": "sha512-ymN2pj6zEjiKJZbrf98UM2pfDd6F2H7ksKw7NDt/ZZ1fh5Ei39X5tABugtT03ZRlWd9imccoK0hE8hpjpU7irQ=="
}
}
},
"vue-demi": {

View File

@ -18,10 +18,10 @@
"dayjs": "^1.11.13",
"element-plus": "^2.9.0",
"mitt": "^3.0.1",
"pinia": "^2.2.4",
"pinia": "^2.0.35",
"pinia-plugin-persistedstate": "^4.1.2",
"querystring": "^0.2.1",
"vue": "^3.4.21",
"vue": "^3.2.31",
"vue-router": "^4.3.3"
},
"devDependencies": {

View File

@ -40,8 +40,8 @@ html{
body{
padding: 0;
margin: 0;
overflow: hidden;
overflow: auto;
width: 100%;
height: 100%;
min-width: 1200px;
}
</style>

View File

@ -8,6 +8,7 @@ const myAxios = axios.create({
// baseURL: 'http://123.249.108.160:8888/api' //测试服务器
// baseURL: 'http://154.8.193.216:9092/api', //隋雨霏服务器
// baseURL: 'http://154.8.193.216:9093/api' //隋雨霏服务器测试环境
// baseURL: 'https://www.carboner.cn:8888/api'
});
// 添加请求拦截器
axios.interceptors.request.use(function (config) {

View File

@ -25,7 +25,7 @@
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="$router.push('/PersonalCenter')">个人信息</el-dropdown-item>
<el-dropdown-item @click="router.push('/PersonalCenter')">个人信息</el-dropdown-item>
<el-dropdown-item @click="logout">退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>

View File

@ -2,28 +2,31 @@
<div style="border: 1px solid #ccc">
<Toolbar style="border-bottom: 1px solid #ccc; width: 400px;" :editor="editorRef" :defaultConfig="toolbarConfig"
:mode="mode" />
<Editor style="height: 500px; overflow-y: hidden; width: 400px;" v-model="context" :defaultConfig="editorConfig"
<Editor style="height: 500px; overflow-y: hidden; width: 400px;" v-model="context" :value="disableRich" :defaultConfig="editorConfig"
:mode="mode" @onChange="handleChange" @onCreated="handleCreated" />
</div>
</template>
<script setup>
import '@wangeditor/editor/dist/css/style.css' // css
import { onBeforeUnmount, ref, shallowRef, onMounted, defineEmits, toRefs } from 'vue'
import { onBeforeUnmount, ref, shallowRef, onMounted, defineEmits } from 'vue'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import axios from "axios";
import myAxios from "@/api/myAxios";
// shallowRef
const editorRef = shallowRef()
// HTML
const valueHtml = ref('')
const emit = defineEmits(['richTextContent']) //
const props = defineProps({
context: String
context: String,
disableRich: Boolean
})
// ajax
// const { context } = toRefs(props) //context
onMounted(() => {
setTimeout(() => {
if(props.context != undefined) {
if(props.context !== undefined) {
valueHtml.value = props.context //访
}
}, 1500)
@ -31,15 +34,31 @@ onMounted(() => {
const toolbarConfig = {}
const editorConfig = {
placeholder: '请输入内容...',
MENU_CONF: {}
}
editorConfig.readOnly = props.disableRich //
editorConfig.MENU_CONF['uploadImage'] = {
placeholder: '请输入内容...',
async customUploadImage(file, insertFn) {
const { data: res } = await axios.post('',{},{})
console.log('res--->',res)
async customUpload(file, insertFn) {
const res = await myAxios({
url: '/file/uploadFile',
method: 'POST',
headers: {
'Content-Type': 'multipart/form-data',
},
data: {
biz: "test",
file: file
}
})
console.log('图片上传返回值--->',res.data)
const downloadUrl = 'http://123.249.108.160:8888/api/file/downloadFile?objectKey=';
insertFn(downloadUrl + res.data.data, res.alt, res.href)
}
}
//
onBeforeUnmount(() => {
const editor = editorRef.value
@ -51,6 +70,7 @@ const handleCreated = (editor) => {
}
const handleChange = (editor) => { //emit
emit('richTextContent', props.context)
// emit('disableChange',props.disable)
// console.log('html',editor.getHtml());
}
</script>

View File

@ -72,6 +72,16 @@ export const constantRoute = [
icon: 'Files',
hidden: false
}
},
{
path: '/EntityProductDetail/:id/:flag',
name: '商品详情',
component: () => import("@/views/Commodity/EntityProductDetail.vue"),
meta: {
title: '商品详情',
icon: 'Files',
hidden: true
}
}
]
},
@ -105,6 +115,15 @@ export const constantRoute = [
icon: 'DataBoard',
hidden: false
}
},
{
path: '/ServiceProductDetail/:id/:flag',
name: '课程详情',
component: () => import("@/views/ServiceType/ServiceProductDetail.vue"),
meta: {
title: '课程详情',
hidden: true
}
}
]
},
@ -151,39 +170,6 @@ export const constantRoute = [
}
]
},
// 优惠卷信息
{
path: '/coupon',
redirect: '/AddCoupons',
component: () => import("@/layout/Manage.vue"),
meta: {
title: '优惠卷管理',
icon: 'Discount',
hidden: false
},
children: [
{
path: '/AddCoupons',
name: '添加优惠券',
component: () => import("@/views/Coupons/AddCoupons.vue"),
meta: {
title: '添加优惠券',
icon: 'Edit',
hidden: false
}
},
{
path: '/CouponCenter',
name: '优惠券中心',
component: () => import("@/views/Coupons/CouponCenter.vue"),
meta: {
title: '优惠券中心',
icon: 'DataAnalysis',
hidden: false
}
}
]
},
// 写真预约
{
path: '/outfitManage',
@ -243,6 +229,48 @@ export const constantRoute = [
title: '写真订单详情',
hidden: true
}
},
{
path: '/OutfitDetail/:id/:flag',
name: '服装详情',
component: () => import("@/views/CostumeAppointments/OutfitDetail.vue"),
meta: {
title: '服装详情',
hidden: true
}
}
]
},
// 优惠卷信息
{
path: '/coupon',
redirect: '/AddCoupons',
component: () => import("@/layout/Manage.vue"),
meta: {
title: '优惠卷管理',
icon: 'Discount',
hidden: false
},
children: [
{
path: '/AddCoupons',
name: '添加优惠券',
component: () => import("@/views/Coupons/AddCoupons.vue"),
meta: {
title: '添加优惠券',
icon: 'Edit',
hidden: false
}
},
{
path: '/CouponCenter',
name: '优惠券中心',
component: () => import("@/views/Coupons/CouponCenter.vue"),
meta: {
title: '优惠券中心',
icon: 'DataAnalysis',
hidden: false
}
}
]
},
@ -300,17 +328,50 @@ export const constantRoute = [
icon: 'User',
hidden: false
}
}
]
},
//节日管理+商城轮播图管理+课程首页轮播图管理
{
path: '/OtherManage',
redirect: '/EntityCarouseCenter',
component: () => import("@/layout/Manage.vue"),
meta: {
title: '其他管理',
icon: '',
hidden: false
},
children: [
{
path: '/EntityCarouseCenter',
name: '商城轮播图管理',
component: () => import("@/views/OtherManagement/EntityCarouseCenter.vue"),
meta: {
title: '商城轮播图管理',
icon: '',
hidden: false
}
},
// {
// path: '/Upload',
// name: '文件上传',
// component: () => import("@/views/User/Upload.vue"),
// meta: {
// title: '文件上传',
// icon: 'Upload',
// hidden: false
// }
// },
{
path: '/CourseCarouseCenter',
name: '课程轮播图管理',
component: () => import("@/views/OtherManagement/CourseCarouseCenter.vue"),
meta: {
title: '课程轮播图管理',
icon: '',
hidden: false
}
},
{
path: '/FestivalManage',
name: '节日管理',
component: () => import("@/views/OtherManagement/FestivalManagement.vue"),
meta: {
title: '节日管理',
icon: '',
hidden: false
}
}
]
},
{

View File

@ -14,19 +14,21 @@ export const form : any = ref({ //添加实体类商品的表单
festivalName: '', //节日名称
type: '',//类别
label: '',//商品标签
introDetail: '',//详情描述
// introDetail: '',//详情描述
goodImg: '', //商品图片url
detailImg: '', //图文详情url
inventory: '' //库存
// detailImg: '', //图文详情url
inventory: '', //库存
richText: ''
})
export const editForm: any = ref({}) //编辑后的实体类商品表格
//图片上传请求 此请求只要选择了图片,就会默认上传
export const handleChange = async (file: any, flag: number ) => {
fileSimple.value = file
let formData = new FormData() //这一步很重要 创建一个FormData对象
formData.append("file", fileSimple.value.raw) //fileSimple.value.raw 才是文件主体 将其以文件的格式插入formData
const res = await myAxios({ //编写请求,与以前的请求不同,这一次要指定好头部类型和文件类型
url: '/file/upload/server/not_login',
url: '/file/uploadFile',
method: 'post',
headers: {
'content-Type': 'multipart/form-data'
@ -37,21 +39,20 @@ export const handleChange = async (file: any, flag: number ) => {
file: formData.get("file")
}
})
// console.log('图片上传的结果--->',res.data)
if (res.data.code === 1) {
if ( flag === 2 ) {
productImgArr.value.splice(0, productImgArr.value.length) //一并删除数组中存放的图片Url地址
productImgMap.set(fileSimple.value.uid, res.data.data)
productImgMap.set(fileSimple.value.uid,res.data.data)
productImgMap.forEach(loopMap) //将图片url插入到数组中
form.value.goodImg = formatString()
} else if(flag === 0 && ImgArr != null) {
dealImgArr(ImgArr.value) //先处理
productImgMap.set(fileSimple.value.uid, res.data.data)
productImgMap.set(fileSimple.value.uid,res.data.data)
productImgMap.forEach(loopMap) //将图片url插入到数组中
pushImgArr(productImgArr.value)
editForm.value.goodImg = formatString()
productImgMap.clear() //清除所有的键防止bug
} else {
form.value.detailImg = res.data.data
}
}
}

View File

@ -1,7 +1,12 @@
<template>
<div class="totalBox">
<div class="flexCard">
<el-card class="richTextPreview" shadow="never">
<div class="titleText">移动端预览</div>
<p v-html="previewRichText"></p>
</el-card>
<!-- 表单 ref prop绑定 用于重置表单 -->
<div class="fromBox">
<el-card class="form-container" shadow="never">
<div class="form">
<el-form ref="resetFormData" :model="form" label-width="auto" size="large" :rules="rules">
<div>
<el-form-item label="添加商品图片">
@ -15,18 +20,6 @@
</el-upload>
</el-form-item>
</div>
<!-- 添加图文描述-->
<div>
<el-form-item label="添加图文描述">
<el-upload ref="uploadProductDetail" action="#" list-type="picture-card" :auto-upload="false"
multiple="true" :on-change="(event: any) => handleChange(event, 1)" :on-exceed="Exceed_ProductDetail"
limit="1" :on-remove="handleRemove">
<el-icon>
<Plus />
</el-icon>
</el-upload>
</el-form-item>
</div>
<div class="NamePrice">
<el-form-item label="产品名称" prop="name">
<el-input v-model="form.name" maxlength="12" minlength="2" show-word-limit style="width: 260px;" />
@ -49,7 +42,9 @@
</div>
<div class="festivalBox">
<el-form-item label="节日名称" prop="festivalName" style="width: 300px;">
<el-input v-model="form.festivalName" />
<el-select v-model="form.festivalName" placeholder="请选择" >
<el-option v-for="item in festivalArr" :key="item" :label="item.name" :value="item.name" />
</el-select>
</el-form-item>
<el-form-item label="产品类别" prop="type" style="width: 240px;">
<el-select v-model="form.type" placeholder="请选择" @change="(event: any) => loadForm(event)" :disabled="typeSelect">
@ -67,8 +62,8 @@
</div>
<el-button type="primary" @click="addLabel">添加</el-button>
</el-form-item>
<el-form-item label="商品详细描述" prop="introDetail">
<el-input v-model="form.introDetail" type="textarea" placeholder="产品尺寸,服务等" maxlength="100" show-word-limit />
<el-form-item label="商品图文" prop="richText">
<richTextUtil @richTextContent="getInfo"/>
</el-form-item>
<div class="btnBox">
<el-form-item>
@ -80,29 +75,44 @@
</div>
</el-form>
</div>
</el-card>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted, inject, reactive } from 'vue';
import { Plus } from '@element-plus/icons-vue';
import { type UploadProps, genFileId, type UploadRawFile, type FormInstance, type FormRules } from 'element-plus';
import { type FormRules } from 'element-plus';
import { SuccessInfo, WarnInfo, CommInfo } from '@/utils/messageInfo'; //ElMessage
import myAxios from "@/api/myAxios";
import { saveBtn, invInput } from '@/utils/entityProduct/globalVar';
import { validateName, validateIntro, validateDetail } from '@/utils/entityProduct/FormRules';
import { handleChange, handleRemove, form, Exceed_ProductImg } from '@/utils/entityProduct/picUpload';
import richTextUtil from '@/layout/components/richTextUtil.vue';
const resetFormData = ref()
const reload: any = inject("reload") //
const typeList: any = ref([])
const uploadProductImg: any = ref() //ref
const uploadProductDetail: any = ref() //ref
const labelList = ref([''])
const isqualify : any = ref(false)
const typeSelect : any = ref(false) //
const previewRichText: any = ref<String>('')
const festivalArr : any = ref([]) //
onMounted(() => {
getTypeList() //select
getFestivalArr() //
})
const getFestivalArr = async () => {
const res = await myAxios.get('/festival/get')
if(res.data.code === 1) {
festivalArr.value = res.data.data
} else {
WarnInfo('获取节日失败,检查服务')
}
}
const getTypeList = async () => { //
const res = await myAxios.post('/category/list/web', {}) //
for(let key in res.data.data) {
@ -118,8 +128,17 @@ const onSubmit = async () => {
WarnInfo('请检查表单数据是否完整填写')
return; //
}
const res = await myAxios.post('/goods/add', { ...form.value })
// console.log(res.data);
const res = await myAxios.post('/goods/add', {
name: form.value.name,
type: form.value.type,
intro: form.value.intro,
price: form.value.price,
goodImg: form.value.goodImg,
richText: form.value.richText,
inventory: form.value.inventory,
festivalName: form.value.festivalName,
label: form.value.label
})
if (res.data.code === 1) {
SuccessInfo('提交成功')
form.value = {} //12.18
@ -137,12 +156,7 @@ const loadForm = (value: any) => {
const resetForm = () => {
resetFormData.value.resetFields()
}
const Exceed_ProductDetail: UploadProps['onExceed'] = (files) => { //
uploadProductDetail.value!.clearFiles() //
const file = files[0] as UploadRawFile
file.uid = genFileId()
uploadProductDetail.value!.handleStart(file) //
}
const rules = reactive<FormRules<typeof form>>({ //
name: [{ validator: validateName, trigger: 'blur' }],
intro: [{ validator: validateIntro, trigger: 'blur' }],
@ -177,17 +191,37 @@ const customTag = (e:any)=>{ //点击定制
form.value.type = ''
}
}
const getInfo = (info: any) => { //
// console.log('info', info)
previewRichText.value = info
form.value.richText = encode64(info)
}
function encode64(text: string): string {
return btoa(String.fromCharCode(...new TextEncoder().encode(text)))
}
</script>
<style scoped>
.totalBox {
.flexCard {
display: flex;
margin: 0 auto;
height: 100%;
width: 1200px;
overflow-x: auto;
}
.fromBox {
.richTextPreview {
height: 80vh;
overflow-y: auto;
word-break: break-all;
width: 400px;
margin-right: 10px;
}
.form-container {
width: 700px;
height: 750px;
/* margin: 0 auto; */
}
.form {
/* width: 500px; */
margin: 0 auto;
}

View File

@ -0,0 +1,282 @@
<template>
<div class="flexCard">
<el-card class="richTextPreview" shadow="never">
<div class="titleText">移动端预览</div>
<p v-html="previewRichText"></p>
</el-card>
<!-- 表单 ref prop绑定 用于重置表单 -->
<el-card class="form-container" shadow="never">
<div class="form">
<el-form ref="resetFormData" :model="detailFrom" label-width="auto" size="large" :rules="rules" :disabled="disabledRichText">
<div>
<el-form-item label="添加商品图片">
<!-- 下面的event的作用,传入当前事件对象 -->
<el-upload v-model:file-list="ImgArr" ref="uploadProductImg" action="#" list-type="picture-card" :auto-upload="false" multiple="true"
:on-remove="handleRemove" @change="(event: any) => handleChange(event, 2)" :on-exceed="Exceed_ProductImg"
limit="7">
<el-icon>
<Plus />
</el-icon>
</el-upload>
</el-form-item>
</div>
<div class="NamePrice">
<el-form-item label="产品名称" prop="name">
<el-input v-model="detailFrom.name" maxlength="12" minlength="2" show-word-limit style="width: 260px;" />
</el-form-item>
<el-form-item label="产品价格" prop="price">
<el-input-number v-model="detailFrom.price" min="0" :precision="2" :step="0.5" />
<p></p>
</el-form-item>
</div>
<div class="IntroInven">
<el-form-item label="产品简介" prop="intro">
<el-input v-model="detailFrom.intro" type="textarea" placeholder="产品尺寸,服务等" maxlength="30" show-word-limit
style="width: 300px;" />
</el-form-item>
<div>
<el-form-item label="库存" prop="inventory">
<el-input-number v-model="detailFrom.inventory" min="0" :precision="0" :step="1" :disabled="invInput" />
</el-form-item>
</div>
</div>
<div class="festivalBox">
<el-form-item label="节日名称" prop="festivalName" style="width: 300px;">
<el-select v-model="detailFrom.festivalName" placeholder="请选择" >
<el-option v-for="item in festivalArr" :key="item" :label="item.name" :value="item.name" />
</el-select>
</el-form-item>
<el-form-item label="产品类别" prop="type" style="width: 240px;">
<el-select v-model="detailFrom.type" placeholder="请选择" @change="(event: any) => loadForm(event)" :disabled="typeSelect">
<el-option v-for="item in typeList" :key="item" :label="item" :value="item" />
</el-select>
</el-form-item>
<div>
<el-checkbox v-model="isqualify" label="定制商品" size="large" border style="margin-left: 10px;" @change="customTag" />
</div>
</div>
<el-form-item label="商品标签" prop="label">
<div v-for="(item, index) in labelList" :key="index">
<el-input v-model="labelList[index]" type="text" style="width: 150px;" maxlength="5" show-word-limit
@blur="addLabelList" />
</div>
<el-button type="primary" @click="addLabel">添加</el-button>
</el-form-item>
<el-form-item label="商品图文" prop="richText">
<richTextUtil @richTextContent="getInfo" v-model:disableRich="disabledRichText" v-model:context="previewRichText"/>
</el-form-item>
<div class="btnBox">
<el-form-item>
<el-button type="primary" @click="onSubmit" :disabled="saveBtn">更新</el-button>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="resetForm">重置</el-button>
</el-form-item>
</div>
</el-form>
</div>
</el-card>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted, inject, reactive, onBeforeMount } from 'vue';
import { Plus } from '@element-plus/icons-vue';
import {type FormRules, type UploadUserFile} from 'element-plus';
import { SuccessInfo, WarnInfo, CommInfo } from '@/utils/messageInfo'; //ElMessage
import myAxios from "@/api/myAxios";
import { saveBtn, invInput } from '@/utils/entityProduct/globalVar';
import { validateName, validateIntro, validateDetail } from '@/utils/entityProduct/FormRules';
import { handleChange, handleRemove, form, Exceed_ProductImg } from '@/utils/entityProduct/picUpload';
import richTextUtil from '@/layout/components/richTextUtil.vue';
import { useRoute } from "vue-router";
const route = useRoute(); //
const resetFormData = ref()
const reload: any = inject("reload") //
const typeList: any = ref([])
const uploadProductImg: any = ref() //ref
const labelList = ref([''])
const isqualify : any = ref(false)
const typeSelect : any = ref(false) //
const ImgArr: any = ref<UploadUserFile[]>([])
const detailFrom : any = ref({
name: '', //
price: '', //
intro: '',//
festivalName: '', //
type: '',//
label: '',//
// introDetail: '',//
goodImg: '', //url
// detailImg: '', //url
inventory: '', //
richText: ''
})
const disabledRichText:any = ref(false);
const previewRichText:any = ref('');
const festivalArr : any = ref([]) //
onBeforeMount(()=>{ //
if(route.params.flag === '0'){
disabledRichText.value = true;
}
})
onMounted(() => {
getTypeList() //select
getProductInfo() //
getFestivalArr()
})
//
const getFestivalArr = async () => {
const res = await myAxios.get('/festival/get')
if(res.data.code === 1) {
festivalArr.value = res.data.data
} else {
WarnInfo('获取节日失败,检查服务')
}
}
const getProductInfo = async () => { //
const res = await myAxios.post('/goods/getById',{ id: route.params.id })
// console.log('--->',res.data.data)
if(res.data.code === 1) {
detailFrom.value = res.data.data
previewRichText.value = JSON.parse(JSON.stringify(res.data.data.richText))
ImgArr.value.splice(0, ImgArr.value.length)
labelList.value = res.data.data.label.split(';')
var tempArr = res.data.data.goodImg.split(';')
tempArr.forEach((item: any) => {
ImgArr.value.push({
url: item
})
})
}
}
const getTypeList = async () => { //
const res = await myAxios.post('/category/list/web', {}) //
for(let key in res.data.data) {
typeList.value.push(res.data.data[key].typeName)
}
}
//
const onSubmit = async () => {
const values = Object.values(detailFrom.value );
// 使some()
if (values.some((value: any) => value === null || value === undefined || value === '' || value === 0)) {
WarnInfo('请检查表单数据是否完整填写')
return; //
}
const res = await myAxios.post('/goods/update', { ...detailFrom.value })
// console.log(res.data);
if (res.data.code === 1) {
SuccessInfo('提交成功')
// detailFrom.value = {}
// reload() //
} else {
WarnInfo('服务错误')
return;
}
}
//
const loadForm = (value: any) => {
detailFrom.value.type = value // flag=1 "" flag=0
}
//
const resetForm = () => {
resetFormData.value.resetFields()
}
const rules = reactive<FormRules<typeof form>>({ //
name: [{ validator: validateName, trigger: 'blur' }],
intro: [{ validator: validateIntro, trigger: 'blur' }],
introDetail: [{ validator: validateDetail, trigger: 'blur' }]
})
const addLabel = () => { //
if (labelList.value[labelList.value.length - 1] === '') {
WarnInfo('请填写完')
return;
}
if (labelList.value.length < 4) {
labelList.value.push('')
}
}
const addLabelList = () => { //
var string = ''
if (!labelList.value.some((item: any) => item === '')) {
string = labelList.value.join(';')
} else return;
detailFrom.value.label = string
}
const customTag = (e:any)=>{ //
if(e) {
detailFrom.value.inventory = 1
invInput.value = true
typeSelect.value = true
detailFrom.value.type = '定制'
} else {
detailFrom.value.inventory = 0
invInput.value = false
typeSelect.value = false
detailFrom.value.type = ''
}
}
const getInfo = (info: any) => { //
// console.log('info', info)
previewRichText.value = info
detailFrom.value.richText = encode64(info)
}
//base64
function encode64(text: string): string {
return btoa(String.fromCharCode(...new TextEncoder().encode(text)))
}
</script>
<style scoped>
.flexCard {
display: flex;
height: 100%;
}
.richTextPreview {
height: 80vh;
overflow-y: auto;
word-break: break-all;
width: 400px;
margin-right: 10px;
}
.form-container {
width: 700px;
/* margin: 0 auto; */
}
.form {
/* width: 500px; */
margin: 0 auto;
}
.NamePrice {
display: flex;
}
.IntroInven {
display: flex;
}
.festivalBox {
display: flex;
}
.btnBox {
display: flex;
justify-content: space-around;
}
</style>

View File

@ -95,105 +95,16 @@
:small="null" :disabled="null" :background="null" layout="total, sizes, prev, pager, next, jumper"
:total="total"/>
</div>
<!-- 编辑/详情表单 -->
<el-dialog v-model="DialogVisible" :title="title">
<el-form label-width="100px" :disabled="disabled" v-model="editForm">
<el-form-item label="商品编号">
<el-input v-model="editForm.id" disabled style="width: 60px;"></el-input>
</el-form-item>
<el-form-item label="商品图片">
<el-upload
v-model:file-list="ImgArr"
ref="uploadProductImg"
action="#"
list-type="picture-card"
:auto-upload="false"
multiple="true"
:on-remove="removePic"
@change="(event: any) => handleChange(event, 0)"
:on-exceed="Exceed_ProductImg"
limit="7">
<el-icon>
<Plus/>
</el-icon>
</el-upload>
</el-form-item>
<el-form-item label="商品名称">
<el-input v-model="editForm.name" style="width: 200px;"></el-input>
</el-form-item>
<el-form-item label="产品价格" prop="price">
<el-input-number v-model="editForm.price" min="0.01" :precision="2" :step="0.5"/>
<p style="margin-left: 5px"></p>
</el-form-item>
<div style="display: flex;">
<el-form-item label="产品类别" prop="type">
<el-select v-model="editForm.type" @change="(event: any) => loadForm(event, 1)" style="width: 120px;">
<el-option v-for="item in typeList" :key="item.value" :label="item.label" :value="item.value1"/>
</el-select>
</el-form-item>
<!-- <el-form-item label="商品数量">
<el-input v-model="editForm.inventory" style="width: 50px;"></el-input>
</el-form-item> -->
<el-form-item label="库存" prop="inventory">
<el-input-number v-model="editForm.inventory" min="0" :precision="0" :step="1"/>
</el-form-item>
</div>
<div style="display: flex;">
<el-form-item label="是否为限定类产品" prop="festivalOrder">
<el-select v-model="editForm.festivalOrder" @change="(event: any) => loadForm(event, 0)" style="width: 70px;">
<el-option label="是" value="1"/>
<el-option label="否" value="0"/>
</el-select>
</el-form-item>
<el-form-item label="商品标签">
<el-input v-model="editForm.label" style="width: auto;"></el-input>
</el-form-item>
</div>
<el-form-item label="商品图文">
<div v-if="editForm.detailImg">
<img :src="editForm.detailImg" alt="商品图标" class="thumbnail" style="width: 100px; height: 100px;"/>
</div>
<el-upload
ref="uploadProductDetail"
action="#"
list-type="picture-card"
:auto-upload="false"
multiple="true"
:on-remove="handleRemove"
@change="(event: any) => handleChange(event, 1)"
:on-exceed="Exceed_ProductDetail"
limit="1">
<el-icon>
<Plus/>
</el-icon>
</el-upload>
</el-form-item>
<el-form-item label="商品描述">
<el-input type="textarea" v-model="editForm.introDetail"></el-input>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="DialogVisible = false" :disabled="disabled">取消</el-button>
<el-button type="primary" @click="saveEdit" :disabled="disabled">确认</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import {ElMessage, type UploadProps, genFileId, type UploadRawFile} from 'element-plus';
import {ref, onMounted, inject} from 'vue';
import myAxios from "@/api/myAxios";
import {
handleChange,
removePic,
handleRemove,
Exceed_ProductImg,
ImgArr,
editForm
} from '@/utils/entityProduct/picUpload';
// import { handleChange, handleRemove, form, Exceed_ProductImg } from '@/utils/entityProduct/picUpload';
import { editForm } from '@/utils/entityProduct/picUpload';
import { useRouter } from "vue-router";
const router = useRouter(); //
const total = ref(0); //
const idList = ref<Number[]>([]);
const searchParams: any = ref({ //
@ -202,15 +113,10 @@ const searchParams: any = ref({ //封装分页
sortField: "id", //ID
sortOrder: "descend" //
})
const disabled = ref(true)
//
const title = ref('') //
const productName = ref('');
const DialogVisible = ref(false);
const state = ref(''); //select
const tableData: any = ref([]); //
const reload: any = inject("reload") //
const uploadProductImg: any = ref() //ref
const uploadProductDetail: any = ref() //ref
const typeList: any = ref([
{
@ -251,19 +157,7 @@ const getTypeList = async () => {
}
}
}
const saveEdit = async () => { //
const res = await myAxios.post('/goods/update', {...editForm.value})
if (res.data.code === 1) {
ElMessage({
type: 'success',
message: '更新成功'
})
DialogVisible.value = false
getProductList()
} else {
ElMessage.error('更新失败,请检查字段')
}
}
const searchByType = (event: any) => { //
searchParams.value.type = event
searchParams.value.current = 1
@ -284,22 +178,19 @@ const deleteProduct = async (index: number) => {
type: 'success',
message: '删除成功',
})
getProductList()
await getProductList()
}
getTypeList()
await getTypeList()
}
//
const ReviseOrView = (index: number, row: any, flag: number) => {
ImgArr.value.splice(0, ImgArr.value.length)
var tempArr = row.goodImg.split(';')
tempArr.forEach((item: any) => {
ImgArr.value.push({
url: item
})
router.push({
name: '商品详情',
params: {
id: row.id,
flag: flag
}
})
flag ? (title.value = "编辑商品" , disabled.value = false) : (title.value = "商品详情" , disabled.value = true)
DialogVisible.value = true;
editForm.value = JSON.parse(JSON.stringify(row))
}
//
const handleOff = async (index: number, row: any) => {
@ -327,21 +218,7 @@ const handleCurrentChange = (Current: any) => {
const reset = () => {
reload()
};
const Exceed_ProductDetail: UploadProps['onExceed'] = (files) => { //
uploadProductDetail.value!.clearFiles()
const file = files[0] as UploadRawFile
file.uid = genFileId()
uploadProductDetail.value!.handleStart(file)
}
//
const loadForm = (value: any, flag: number) => {
flag ? editForm.value.type = value : editForm.value.festivalOrder = value // flag=1 "" flag=0
}
const onSearch = (data: String) => { //
searchParams.value.name = data
searchParams.value.current = 1
getProductList()
}
//
const delBatch = async () => {
console.log(idList.value);
@ -368,6 +245,9 @@ const selectChange = (selection: any, row: any) => { //selction是对象数组
})
// console.log('idList--->',idList.value);
}
//
const onSearch = (value: string) => {}
</script>
<style scoped>

View File

@ -112,7 +112,7 @@ const handleChange = async (file: any) => {
let formData = new FormData() // FormData
formData.append("file", fileSimple.value.raw) //fileSimple.value.raw formData
const res = await myAxios({ //
url: '/file/upload/server/not_login',
url: '/file/uploadFile',
method: 'post',
headers: {
'content-Type': 'multipart/form-data'

View File

@ -1,72 +1,57 @@
<template>
<div class="totalBox">
<div class="fromBox">
<!-- 表单 ref prop绑定 用于重置表单 -->
<el-form ref="resetFormData" :model="form" label-width="auto" style="width: 750px;" size="large">
<div class="totalPicture">
<div>
<el-form-item label="添加写真展示图片">
<!-- 下面的event的作用,传入当前事件对象 -->
<el-upload ref="uploadProductImg" action="#" list-type="picture-card" :auto-upload="false" multiple="true"
:on-remove="handleRemove" @change="(event: any) => handleChange(event, 0)"
:on-exceed="Exceed_ProductImg" limit="1">
<el-icon>
<Plus />
</el-icon>
</el-upload>
</el-form-item>
</div>
<!-- 添加图文描述-->
<div>
<el-form-item label="添加服装详情图">
<el-upload ref="uploadProductDetail" action="#" list-type="picture-card" :auto-upload="false"
multiple="true" :on-change="(event: any) => handleChange(event, 1)" :on-exceed="Exceed_ProductDetail"
limit="1" :on-remove="handleRemove">
<el-icon>
<Plus />
</el-icon>
</el-upload>
</el-form-item>
</div>
</div>
<div class="NamePrice">
<div class="name">
<el-form-item label="服装名称" prop="name">
<el-input v-model="form.name" />
</el-form-item>
</div>
<div class="price">
<el-form-item label="价格" prop="price">
<el-input-number v-model="form.price" min="0" :precision="2" :step="0.5" />
<p></p>
</el-form-item>
</div>
</div>
<!-- 拍摄场地户外室内均可 -->
<div class="kinds">
<el-form-item label="拍摄场地" prop="type" style="width: 240px;">
<div class="flexCard">
<el-card class="richTextPreview" shadow="never">
<div class="titleText">移动端预览</div>
<p v-html="form.richText"></p>
</el-card>
<!-- 表单 ref prop绑定 用于重置表单 -->
<el-card class="form-FormContainer" shadow="never">
<el-form ref="resetFormData" :model="form" label-width="auto" size="large">
<el-form-item label="添加写真展示图片">
<!-- 下面的event的作用,传入当前事件对象 -->
<el-upload ref="uploadProductImg" action="#" list-type="picture-card" :auto-upload="false" multiple="true"
:on-remove="handleRemove" @change="(event: any) => handleChange(event, 0)"
:on-exceed="Exceed_ProductImg" limit="1">
<el-icon>
<Plus />
</el-icon>
</el-upload>
</el-form-item>
<el-form-item label="服装名称" prop="name" style="width: 300px;" label-width="70px">
<el-input v-model="form.name" />
</el-form-item>
<el-form-item label="价格" prop="price" label-width="70px">
<el-input-number v-model="form.price" min="0" :precision="2" :step="0.5" />
<p></p>
</el-form-item>
<!-- 拍摄场地户外室内均可 -->
<div class="kinds">
<el-form-item label="拍摄场地" prop="type" style="width: 240px;" label-width="70px">
<el-select v-model="form.shotScene" placeholder="请选择" @change="(event: any) => loadScene(event)" >
<el-option v-for="item in siteList" :key="item" :label="item" :value="item" />
</el-select>
</el-form-item>
<el-form-item label="服装类别" prop="type" style="width: 240px;">
<el-form-item label="服装类别" prop="type" style="width: 240px;" label-width="80px">
<el-select v-model="form.categoryName" placeholder="请选择" @change="(event: any) => loadCategory(event)" >
<el-option v-for="item in typeList" :key="item" :label="item" :value="item" />
</el-select>
</el-form-item>
</div>
<div class="numberSelect">
<el-form-item label="最小人数" prop="type" style="width: 240px;">
</div>
<div class="numberSelect">
<el-form-item label="最小人数" prop="type" style="width: 240px;" label-width="70px">
<el-select v-model="form.minNumber" @change="minNum()">
<el-option v-for="item in minArr" :label="item" :value="item" />
</el-select>
<el-option v-for="item in minArr" :label="item" :value="item" />
</el-select>
</el-form-item>
<el-form-item label="最大人数" prop="type" style="width: 240px;">
<el-form-item label="最大人数" prop="type" style="width: 240px;" label-width="80px">
<el-select v-model="form.maxNumber" @change="maxNum()">
<el-option v-for="item in maxArr" :label="item" :value="item" />
</el-select>
<el-option v-for="item in maxArr" :label="item" :value="item" />
</el-select>
</el-form-item>
</div>
</div>
<el-form-item label="效果图" label-width="70px">
<richTextUtil @richTextContent="getInfo"/>
</el-form-item>
<div class="totalButton">
<el-form-item>
<el-button type="primary" @click="onSubmit">保存</el-button>
@ -76,24 +61,23 @@
</el-form-item>
</div>
</el-form>
</div>
<div>
<!-- <appointTime @time-Info="getInfo"></appointTime> -->
</el-card>
<el-card class="form-RichTextContainer" shadow="never">
<outfitCalendarUtil @calendar-info="getInfo"></outfitCalendarUtil>
</div>
</el-card>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, inject, toRaw, effect } from 'vue';
import { Plus } from '@element-plus/icons-vue';
import { ElMessage, type UploadFile, type UploadProps, genFileId, type UploadRawFile } from 'element-plus';
import { ElMessage, type UploadProps, genFileId, type UploadRawFile } from 'element-plus';
import myAxios from "@/api/myAxios";
import outfitCalendarUtil from '@/layout/components/outfitCalendarUtil.vue';
import richTextUtil from '@/layout/components/richTextUtil.vue';
const fileSimple = ref() //
const uploadedFiles = ref<UploadFile[]>([]);//
const uploadedDescription = ref<UploadFile[]>([]);//
const resetFormData = ref()
const form = ref({
name: '', //
@ -101,16 +85,14 @@ const form = ref({
shotScene: '', //
categoryName: '', //
goodImg: '', //url
detailImg: '', //url
minNumber: 0, //
maxNumber: 0, //
effectImg: ["21312312312"], //
richText: '',
bookingDateAddRequestList: [] //
})
//
const reload: any = inject("reload")
const uploadProductImg: any = ref() //ref
const uploadProductDetail: any = ref() //ref
const typeList = ref<String[]>([]) //
const siteList = ref<String[]>(['室外','室内','均可']) //
const minArr = ref<Number[]>([]) //
@ -134,7 +116,7 @@ const loadScene = (value: any) => {
form.value.shotScene = value // flag=1 "" flag=0
}
//
const onSubmit = async () => {
const onSubmit = async () => {
const values : any = Object.values(form.value);
// 使some()
console.log(form.value.bookingDateAddRequestList)
@ -146,7 +128,7 @@ const onSubmit = async () => {
return; //
}
console.log(form.value);
const res = await myAxios.post('/photoProducts/add', {
const res = await myAxios.post('/photoProducts/add', {
name: form.value.name,
price: form.value.price,
minNumber: form.value.minNumber,
@ -154,8 +136,6 @@ const onSubmit = async () => {
shotScene: form.value.shotScene,
categoryName: form.value.categoryName,
introImg: form.value.goodImg,
detailImg: form.value.detailImg,
effectImg: toRaw(form.value.effectImg),
bookingDateAddRequestList: toRaw(form.value.bookingDateAddRequestList)
})
console.log(res.data);
@ -176,7 +156,7 @@ const handleChange = async (file: any, flag: number) => {
let formData = new FormData() // FormData
formData.append("file", fileSimple.value.raw) //fileSimple.value.raw formData
const res = await myAxios({ //
url: '/file/upload/server/not_login',
url: '/file/uploadFile',
method: 'post',
headers: {
'content-Type': 'multipart/form-data'
@ -188,8 +168,9 @@ const handleChange = async (file: any, flag: number) => {
}
})
if (res.data.code === 1) {
form.value.goodImg =res.data.data
// flag0 flag1
flag ? form.value.detailImg = res.data.data : form.value.goodImg = res.data.data
// flag ? form.value.detailImg = res.data.data : form.value.goodImg = res.data.data
}
}
//
@ -202,23 +183,20 @@ const Exceed_ProductImg: UploadProps['onExceed'] = (files) => { //覆盖商品
file.uid = genFileId()
uploadProductImg.value!.handleStart(file)
}
const Exceed_ProductDetail: UploadProps['onExceed'] = (files) => { //
uploadProductDetail.value!.clearFiles()
const file = files[0] as UploadRawFile
file.uid = genFileId()
uploadProductDetail.value!.handleStart(file)
}
const getInfo =(info:any)=>{
console.log('info-->',info);
// timeInfo.value = info
form.value.bookingDateAddRequestList = info
form.value.richText = info
}
const getOutfitKind = async ()=> { //
const res = await myAxios.post('/photoCategory/list',{})
for(let key in res.data.data) {
typeList.value.push(res.data.data[key].name)
}
}
const minNum =()=>{ //
if(form.value.minNumber > form.value.maxNumber && form.value.maxNumber != 0) {
ElMessage({
@ -239,60 +217,43 @@ const maxNum =()=>{
return;
}
}
</script>
<style scoped>
.totalBox {
display: flex;
.form-FormContainer {
width: 600px;
/* margin: 0 auto; */
}
.fromBox {
width: 700px;
height: 750px;
.form-RichTextContainer {
width: 550px;
}
.totalPicture {
.flexCard {
display: flex;
height: 100%;
}
.NamePrice {
display: flex;
.richTextPreview {
height: 80vh;
overflow-y: auto;
word-break: break-all;
width: 400px;
margin-right: 10px;
}
.kinds {
display: flex;
}
.totalButton {
display: flex;
justify-content: space-evenly;
/* 盒子里面水平分隔开 */
}
.midBox {
display: flex;
}
.date-picker {
flex: 0.3;
/* 日期选择区域占据左边 */
display: inline-block;
margin-bottom: 10px;
}
.father {
display: flex;
justify-content: space-around;
align-items: center;
border: 1px solid red;
}
.box {
margin-bottom: 20px;
/* 每个日期或时间段之间的间距 */
border: 1px solid yellow;
}
.inner {
margin-bottom: 10px;
border: 1px solid orange;
}
.numberSelect {
display: flex;
}

View File

@ -29,7 +29,7 @@
详情
</el-button>
<el-popconfirm confirm-button-text='' cancel-button-text='' icon="InfoFilled" icon-color="red"
title="是否取消订单?" @confirm="deleteOrder(scope.row)" width=180 v-if="['待支付'].includes(scope.row.orderStatus)">
title="是否取消订单?" @confirm="cancelOrder(scope.row)" width=180 v-if="['待支付'].includes(scope.row.orderStatus)">
<template #reference>
<el-button size="small" type="primary" plain>取消订单</el-button>
</template>
@ -96,7 +96,7 @@ const getOrderList = async () => {
try {
searchParams.value.orderNumber = orderNumber.value
const res = await myAxios.post('/advanceOrder/list', { ...searchParams.value });
console.log('订单信息--->', res.data.data);
// console.log('--->', res.data.data);
if (res.data.code === 1) {
tableData.value = res.data.data.records;
total.value = parseInt(res.data.data.total)

View File

@ -50,22 +50,16 @@ import myAxios from '@/api/myAxios';
const fileSimple = ref() //
const selectedItems = ref([])
const dialogVisible = ref(false)
// const submitable = ref(true) //
const loading = ref(false) //
const typeImg: any = ref<UploadUserFile[]>([]) //
// const imgMap = new Map() //map
//
const typeForm = ref({
id: 0,
name: '',
// typeIntro: ''
name: ''
})
//
const tableData = ref([])
// (0) (1)
const flag = ref(0)
const typeList = ref([])
//
onMounted(() => {
getTypeList()
})

View File

@ -254,8 +254,10 @@ import {ref, onMounted, inject, toRaw} from 'vue';
import myAxios from "@/api/myAxios";
import outfitCalendarUtil from '@/layout/components/outfitCalendarUtil.vue';
import emitter from "@/utils/emitter";
import {form} from "@/utils/entityProduct/picUpload";
import { useRouter } from "vue-router";
import {WarnInfo} from "@/utils/messageInfo";
const router = useRouter(); //
const total = ref(0); //
const idList = ref<Number[]>([]); //
const searchParams: any = ref({ //
@ -386,6 +388,13 @@ const deleteProduct = async (index: number) => {
}
//
const ReviseOrView = (index: number, row: any , flag : number) => {
// router.push({
// name: '',
// params: {
// id: row.id,
// flag: flag
// }
// })
//flag disabled
flag ? (title.value = "编辑服装" , disabled.value = false) : (title.value = "服装详情" , disabled.value = true)
DialogVisible.value = true;
@ -456,7 +465,7 @@ const handleChange = async (file: any, flag: number) => {
let formData = new FormData() // FormData
formData.append("file", fileSimple.value.raw) //fileSimple.value.raw formData
const res = await myAxios({ //
url: '/file/upload/server/not_login',
url: '/file/uploadFile',
method: 'post',
headers: {
'content-Type': 'multipart/form-data'

View File

@ -0,0 +1,257 @@
<template>
<div class="flexCard">
<el-card class="richTextPreview" shadow="never">
<div class="titleText">移动端预览</div>
<p v-html="detailFrom.richText"></p>
</el-card>
<!-- 表单 ref prop绑定 用于重置表单 -->
<el-card class="form-container" shadow="never">
<div class="form">
<el-form ref="resetFormData" :model="detailFrom" label-width="auto" size="large" :rules="rules" :disabled="true">
<div>
<el-form-item label="添加商品图片">
<!-- 下面的event的作用,传入当前事件对象 -->
<el-upload v-model:file-list="ImgArr" ref="uploadProductImg" action="#" list-type="picture-card" :auto-upload="false" multiple="true"
:on-remove="handleRemove" @change="(event: any) => handleChange(event, 2)" :on-exceed="Exceed_ProductImg"
limit="7">
<el-icon>
<Plus />
</el-icon>
</el-upload>
</el-form-item>
</div>
<div class="NamePrice">
<el-form-item label="产品名称" prop="name">
<el-input v-model="detailFrom.name" maxlength="12" minlength="2" show-word-limit style="width: 260px;" />
</el-form-item>
<el-form-item label="产品价格" prop="price">
<el-input-number v-model="detailFrom.price" min="0" :precision="2" :step="0.5" />
<p></p>
</el-form-item>
</div>
<div class="IntroInven">
<el-form-item label="产品简介" prop="intro">
<el-input v-model="detailFrom.intro" type="textarea" placeholder="产品尺寸,服务等" maxlength="30" show-word-limit
style="width: 300px;" />
</el-form-item>
<div>
<el-form-item label="库存" prop="inventory">
<el-input-number v-model="detailFrom.inventory" min="0" :precision="0" :step="1" :disabled="invInput" />
</el-form-item>
</div>
</div>
<div class="festivalBox">
<el-form-item label="节日名称" prop="festivalName" style="width: 300px;">
<el-input v-model="detailFrom.festivalName" />
</el-form-item>
<el-form-item label="产品类别" prop="type" style="width: 240px;">
<el-select v-model="detailFrom.type" placeholder="请选择" @change="(event: any) => loadForm(event)" :disabled="typeSelect">
<el-option v-for="item in typeList" :key="item" :label="item" :value="item" />
</el-select>
</el-form-item>
<div>
<el-checkbox v-model="isqualify" label="定制商品" size="large" border style="margin-left: 10px;" @change="customTag" />
</div>
</div>
<el-form-item label="商品标签" prop="label">
<div v-for="(item, index) in labelList" :key="index">
<el-input v-model="labelList[index]" type="text" style="width: 150px;" maxlength="5" show-word-limit
@blur="addLabelList" />
</div>
<el-button type="primary" @click="addLabel">添加</el-button>
</el-form-item>
<el-form-item label="商品图文" prop="richText">
<richTextUtil @richTextContent="getInfo" v-model:disable="disabledRichText"/>
</el-form-item>
<div class="btnBox">
<el-form-item>
<el-button type="primary" @click="onSubmit" :disabled="saveBtn">更新</el-button>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="resetForm">重置</el-button>
</el-form-item>
</div>
</el-form>
</div>
</el-card>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted, inject, reactive, onBeforeMount } from 'vue';
import { Plus } from '@element-plus/icons-vue';
import {type FormRules, type UploadUserFile} from 'element-plus';
import { SuccessInfo, WarnInfo, CommInfo } from '@/utils/messageInfo'; //ElMessage
import myAxios from "@/api/myAxios";
import { saveBtn, invInput } from '@/utils/entityProduct/globalVar';
import { validateName, validateIntro, validateDetail } from '@/utils/entityProduct/FormRules';
import { handleChange, handleRemove, form, Exceed_ProductImg } from '@/utils/entityProduct/picUpload';
import richTextUtil from '@/layout/components/richTextUtil.vue';
import { useRoute } from "vue-router";
const route = useRoute(); //
const resetFormData = ref()
const reload: any = inject("reload") //
const typeList: any = ref([])
const uploadProductImg: any = ref() //ref
const labelList = ref([''])
const isqualify : any = ref(false)
const typeSelect : any = ref(false) //
const ImgArr: any = ref<UploadUserFile[]>([])
const detailFrom : any = ref({
name: '', //
price: '', //
intro: '',//
categoryName: '', //
goodImg: '', //url
minNumber: 0,
maxNumber: 0,
richText: '',
shotScene: '' //
})
const disabledRichText:any = ref(false);
onBeforeMount(()=>{ //
if(route.params.flag === '0'){
disabledRichText.value = true;
}
})
onMounted(() => {
getTypeList() //select
getProductInfo() //
// console.log('id---->',route.params.id)
// console.log('flag---->',typeof (route.params.flag))
})
const getProductInfo = async () => {
const res = await myAxios.post('/goods/getById',{ id: route.params.id })
console.log('后端查询商品--->',res.data.data)
if(res.data.code === 1) {
detailFrom.value = res.data.data
ImgArr.value.splice(0, ImgArr.value.length)
labelList.value = res.data.data.label.split(';')
var tempArr = res.data.data.goodImg.split(';')
tempArr.forEach((item: any) => {
ImgArr.value.push({
url: item
})
})
}
}
const getTypeList = async () => { //
const res = await myAxios.post('/category/list/web', {}) //
for(let key in res.data.data) {
typeList.value.push(res.data.data[key].typeName)
}
}
//
const onSubmit = async () => {
const values = Object.values(detailFrom.value );
// 使some()
if (values.some((value: any) => value === null || value === undefined || value === '' || value === 0)) {
WarnInfo('请检查表单数据是否完整填写')
return; //
}
const res = await myAxios.post('/goods/add', { ...detailFrom.value })
// console.log(res.data);
if (res.data.code === 1) {
SuccessInfo('提交成功')
detailFrom.value = {} //12.18
reload() //
} else {
WarnInfo('服务错误')
return;
}
}
//
const loadForm = (value: any) => {
detailFrom.value.type = value // flag=1 "" flag=0
}
//
const resetForm = () => {
resetFormData.value.resetFields()
}
const rules = reactive<FormRules<typeof form>>({ //
name: [{ validator: validateName, trigger: 'blur' }],
intro: [{ validator: validateIntro, trigger: 'blur' }],
introDetail: [{ validator: validateDetail, trigger: 'blur' }]
})
const addLabel = () => { //
if (labelList.value[labelList.value.length - 1] === '') {
WarnInfo('请填写完')
return;
}
if (labelList.value.length < 4) {
labelList.value.push('')
}
}
const addLabelList = () => { //
var string = ''
if (!labelList.value.some((item: any) => item === '')) {
string = labelList.value.join(';')
} else return;
detailFrom.value.label = string
}
const customTag = (e:any)=>{ //
if(e) {
detailFrom.value.inventory = 1
invInput.value = true
typeSelect.value = true
detailFrom.value.type = '定制'
} else {
detailFrom.value.inventory = 0
invInput.value = false
typeSelect.value = false
detailFrom.value.type = ''
}
}
const getInfo = (info: any) => { //
console.log('info', info)
detailFrom.value.richText = info
}
</script>
<style scoped>
.flexCard {
display: flex;
height: 100%;
}
.richTextPreview {
height: 80vh;
overflow-y: auto;
word-break: break-all;
width: 400px;
margin-right: 10px;
}
.form-container {
width: 700px;
/* margin: 0 auto; */
}
.form {
/* width: 500px; */
margin: 0 auto;
}
.NamePrice {
display: flex;
}
.IntroInven {
display: flex;
}
.festivalBox {
display: flex;
}
.btnBox {
display: flex;
justify-content: space-around;
}
</style>

View File

@ -40,12 +40,10 @@
<script lang="ts" setup>
import { ref,onMounted} from 'vue'
import {Plus} from "@element-plus/icons-vue";
import {ElMessage, type UploadProps} from "element-plus";
import myAxios from "@/api/myAxios";
const fileSimple = ref()
const form = ref({
name: '',
standardAmount:0,//
@ -78,10 +76,6 @@ const AddCoupon = async () => {
}
}
const handleRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => {
console.log(uploadFile, uploadFiles)
}
</script>

View File

@ -120,10 +120,8 @@ const DialogVisible = ref(false)
const idList = ref([])
const total = ref(0)
const tableData : any= ref([])
const Couponsname = ref('')
const title = ref('')
const editForm : any = ref({})
const fileSimple = ref()
const disabled = ref(true)
const searchParams: any = ref({ //
current: 1,
@ -209,25 +207,7 @@ const getCouponsList = async ()=> {
})
}
}
const handleChange = async (file: any, flag: number) => {
fileSimple.value = file
let formData = new FormData() // FormData
formData.append("file", fileSimple.value.raw) //fileSimple.value.raw formData
const res = await myAxios({ //
url: '/file/upload/server/not_login',
method: 'post',
headers: {
'content-Type': 'multipart/form-data'
},
data: {
biz: "test",
file: formData.get("file") // formDatafile
}
})
if (res.data.code === 1) {
editForm.value.image = res.data.data
}
}
const deleteCoupons = async (index: number) => {
console.log(typeof index)
const res = await myAxios.post('/coupon/delete', { id: index })

View File

@ -31,6 +31,7 @@
" @click="Login">登录</div>
</div>
</el-form>
<a href="https://beian.miit.gov.cn/" target="_blank">粤ICP备2024274839号</a>
</div>
</div>
</div>

View File

@ -145,9 +145,6 @@
<el-form-item label="下单时间" style="width: 300px;">
<el-input v-model="editForm.createTime" autocomplete="off"></el-input>
</el-form-item>
<!-- <el-form-item label="更新时间" style="width: 300px;">-->
<!-- <el-input v-model="editForm.updateTime" autocomplete="off"></el-input>-->
<!-- </el-form-item>-->
</div>
</el-form>
<template #footer>

View File

@ -85,7 +85,7 @@
<el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange"
:current-page="searchParams.current" :page-size="searchParams.pageSize" :page-sizes="[5, 10, 15, 20]"
:small="null" :disabled="null" :background="null" layout="total, sizes, prev, pager, next, jumper"
:total="total" />
:total="totalPage" />
</div>
<!-- 弹窗-->
<el-dialog title="待处理订单详情信息" v-model="DialogVisible" width="60%">
@ -158,7 +158,7 @@ import TimePicker from "@/layout/components/TimePicker.vue";
import emitter from "@/utils/emitter";
import { checkObjectProperties } from "@/layout/js/validUtils";
const tableData: any = ref([]); //
const total = ref(0);
const totalPage = ref(0);
const title = ref('')
const disabled = ref(true)
const editForm: any = ref({});//
@ -174,11 +174,11 @@ const timeSlot = ref('')
const searchParams: any = ref({ //
current: 1, //
pageSize: 5, //
sortField: "id", //ID
// sortField: "id", //ID
sortOrder: "descend", //
goodId: goodId.value,
reservationDate: reservationDate.value,
timeSlot: timeSlot.value
// goodId: goodId.value,
// reservationDate: reservationDate.value,
// timeSlot: timeSlot.value
})
const loading = ref(false)
onMounted(() => {
@ -211,6 +211,7 @@ const getOrderList = async () => {
const res = await myAxios.post('pending/list/advance/detail', { ...searchParams.value });
if (res.data.code === 1) {
tableData.value = res.data.data.records;
totalPage.value = parseInt(res.data.data.total);
console.log(tableData.value)
tableData.value.forEach((item: any) => {
let total = 0;

View File

@ -0,0 +1,214 @@
<template>
<div>
<div style="margin: 10px 0">
<el-input style="width: 200px; height: 30px; margin-right: 10px;" suffix-icon="Search" placeholder="请输入图片名称" v-model="simpleList.name"></el-input>
<el-button class="ml-5" type="primary" @click="onSearch" style="height: 25px;">搜索</el-button>
<el-button type="warning" @click="reset" style="height:25px">重置</el-button>
</div>
<div style="margin: 15px 0">
<el-popconfirm
confirm-button-text='确定'
cancel-button-text='取消'
icon="InfoFilled"
icon-color="red"
title="您确定批量删除这些数据吗?"
@confirm=""
width=180
>
<template #reference>
<el-button style="height: 25px" type="danger" slot="reference">批量删除 <el-icon style="margin-left: 5px;"><Remove /></el-icon></el-button>
</template>
</el-popconfirm>
</div>
<el-table :data="simpleList" border stripe header-cell-class-name="headerBg" :cell-style="{textAlign: 'center'}"
@selection-change="handleSelectionChange" :header-cell-style="{'text-align': 'center'}">
<el-table-column type="selection" width="55"></el-table-column>
<el-table-column prop="id" label="图片序号" width="50"></el-table-column>
<el-table-column prop="imgUrl" label="图片">
<template #default="scope"><img :src="scope.row.imgUrl" alt="" style="height: 50px;"></template>
</el-table-column>
<el-table-column prop="name" label="商品图片" ></el-table-column>
<el-table-column prop="tips" label="对应商品"></el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button type="success" @click="editCarouse(scope.row)">编辑<i class="el-icon-edit"></i></el-button>
<el-popconfirm
confirm-button-text='确定'
cancel-button-text='取消'
icon="el-icon-info"
icon-color="red"
title="您确定删除吗?"
@confirm="deleteCarouse"
>
<template #reference>
<el-button type="danger">删除</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<div style="padding: 10px 0">
<!-- 分页器 -->
<el-pagination
:current-page="searchParams.current"
:page-size="searchParams.pageSize"
:page-sizes="[2, 5, 10, 20]"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:small="null"
background
layout="total, sizes, prev, pager, next, jumper"
:total="total"
/>
</div>
</div>
<!-- 编辑/详情表单 -->
<el-dialog v-model="DialogVisible" title="轮播图编辑">
<el-form label-width="100px" :disabled="disabled">
<el-form-item label="轮播图">
<el-upload
v-model:file-list="carouseImg"
ref="uploadImg"
action="#"
list-type="picture-card"
:auto-upload="false"
multiple="true"
:on-remove="removePic"
@change="(event: any) => handleChange(event)"
:on-exceed="Exceed_ProductImg"
limit="1" >
<el-icon>
<Plus/>
</el-icon>
</el-upload>
</el-form-item>
<el-form-item label="图片名称">
<el-input v-model="editForm.productImg" style="width: 200px;"></el-input>
</el-form-item>
<el-form-item label="对应商品" prop="price">
<el-select v-model="editForm.prdouctName" @change="(event: any) => loadForm(event)" style="width: 120px;">
<el-option v-for="item in goodList" :key="item" :label="item" :value="item" />
</el-select>
</el-form-item>
</el-form>
<template #footer >
<span class="dialog-footer">
<el-button @click="DialogVisible = false" :disabled="disabled">取消</el-button>
<el-button type="primary" @click="saveEdit" :disabled="disabled">确认</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { type UploadProps , type UploadUserFile , genFileId ,type UploadRawFile } from 'element-plus';
import myAxios from '@/api/myAxios';
import {ref, onMounted , inject} from 'vue'
import { WarnInfo, SuccessInfo , ErrorInfo, CommInfo } from '@/utils/messageInfo';
const simpleList : any = ref([
{
id: '1', //id
name: '样例1', //
imgUrl: 'https://carbon2.obs.cn-north-4.myhuaweicloud.com:443/feiyi%2Ftest%2F0%2FgGwBQzmm-cxz.png', //
grade: '等级1', //
correspondingGoods: '非遗发簪' //
},
{
id: '2', //id
name: '样例2', //
imgUrl: 'https://carbon2.obs.cn-north-4.myhuaweicloud.com:443/feiyi%2Ftest%2F0%2FgGwBQzmm-cxz.png', //
grade: '等级2', //
correspondingGoods: '非遗螺钿' //
}
])
const goodList = ref([]) //
const DialogVisible = ref(false); //
const disabled = ref(false)
const editForm : any= ref({})
const uploadImg : any = ref() //ref
const carouseImg: any = ref<UploadUserFile[]>([]) //使
const tableData = ref([]) //
const total = ref(0) //
const fileSimple = ref() //
//
const searchParams: any = ref({
//
current: 1,
//
pageSize: 5
})
//
const reload : any = inject("reload")
onMounted(() => {
})
//
const reset =()=>{
reload()
}
const handleSelectionChange = (val:any)=>{
console.log(val)
}
//
const handleSizeChange = (newSize:any) => {
searchParams.value.pageSize = newSize //
//
};
//
const handleCurrentChange = (Current:any) => {
searchParams.value.current = Current //
//
};
//
const onSearch = (data : String)=>{
searchParams.value.name = data
searchParams.value.current = 1
//
}
const deleteCarouse = async (index: number,row: any) =>{ //
simpleList.value.splice(index,1)
}
const editCarouse = async (obj: any) =>{
carouseImg.value.splice(0,carouseImg.value.length)
carouseImg.value.push({ //
url: obj.imgUrl
})
editForm.value = obj
DialogVisible.value = true
}
const saveEdit = ()=>{ //
DialogVisible.value = false
}
const removePic : UploadProps['onRemove'] = (uploadFile, uploadFiles) => { //uploadFileuploadFiles
}
//
const loadForm = (value: any) => {
editForm.value.grade = value
}
const Exceed_ProductImg: UploadProps['onExceed'] = (files) => { // 'onExceed'
WarnInfo('请移除之前的再上传')
}
const handleChange = async (file: any ) => {
fileSimple.value = file
let formData = new FormData() // FormData
formData.append("file", fileSimple.value.raw) //fileSimple.value.raw formData
// const res = await myAxios({ //
// url: '/file/uploadFile',
// method: 'post',
// headers: {
// 'content-Type': 'multipart/form-data'
// },
// data: {
// biz: "test",
// // formDatafile
// file: formData.get("file")
// }
// })
}
</script>
<style scoped>
.headerBg{
background-color: #eee !important;
}
</style>

View File

@ -0,0 +1,214 @@
<template>
<div>
<div style="margin: 10px 0">
<el-input style="width: 200px; height: 30px; margin-right: 10px;" suffix-icon="Search" placeholder="请输入图片名称" v-model="simpleList.name"></el-input>
<el-button class="ml-5" type="primary" @click="onSearch" style="height: 25px;">搜索</el-button>
<el-button type="warning" @click="reset" style="height:25px">重置</el-button>
</div>
<div style="margin: 15px 0">
<el-popconfirm
confirm-button-text='确定'
cancel-button-text='取消'
icon="InfoFilled"
icon-color="red"
title="您确定批量删除这些数据吗?"
@confirm=""
width=180
>
<template #reference>
<el-button style="height: 25px" type="danger" slot="reference">批量删除 <el-icon style="margin-left: 5px;"><Remove /></el-icon></el-button>
</template>
</el-popconfirm>
</div>
<el-table :data="simpleList" border stripe header-cell-class-name="headerBg" :cell-style="{textAlign: 'center'}"
@selection-change="handleSelectionChange" :header-cell-style="{'text-align': 'center'}">
<el-table-column type="selection" width="55"></el-table-column>
<el-table-column prop="id" label="图片序号" width="50"></el-table-column>
<el-table-column prop="imgUrl" label="图片">
<template #default="scope"><img :src="scope.row.imgUrl" alt="" style="height: 50px;"></template>
</el-table-column>
<el-table-column prop="name" label="商品图片" ></el-table-column>
<el-table-column prop="tips" label="对应商品"></el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button type="success" @click="editCarouse(scope.row)">编辑<i class="el-icon-edit"></i></el-button>
<el-popconfirm
confirm-button-text='确定'
cancel-button-text='取消'
icon="el-icon-info"
icon-color="red"
title="您确定删除吗?"
@confirm="deleteCarouse"
>
<template #reference>
<el-button type="danger">删除</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<div style="padding: 10px 0">
<!-- 分页器 -->
<el-pagination
:current-page="searchParams.current"
:page-size="searchParams.pageSize"
:page-sizes="[2, 5, 10, 20]"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:small="null"
background
layout="total, sizes, prev, pager, next, jumper"
:total="total"
/>
</div>
</div>
<!-- 编辑/详情表单 -->
<el-dialog v-model="DialogVisible" title="轮播图编辑">
<el-form label-width="100px" :disabled="disabled">
<el-form-item label="轮播图">
<el-upload
v-model:file-list="carouseImg"
ref="uploadImg"
action="#"
list-type="picture-card"
:auto-upload="false"
multiple="true"
:on-remove="removePic"
@change="(event: any) => handleChange(event)"
:on-exceed="Exceed_ProductImg"
limit="1" >
<el-icon>
<Plus/>
</el-icon>
</el-upload>
</el-form-item>
<el-form-item label="图片名称">
<el-input v-model="editForm.productImg" style="width: 200px;"></el-input>
</el-form-item>
<el-form-item label="对应商品" prop="price">
<el-select v-model="editForm.prdouctName" @change="(event: any) => loadForm(event)" style="width: 120px;">
<el-option v-for="item in goodList" :key="item" :label="item" :value="item" />
</el-select>
</el-form-item>
</el-form>
<template #footer >
<span class="dialog-footer">
<el-button @click="DialogVisible = false" :disabled="disabled">取消</el-button>
<el-button type="primary" @click="saveEdit" :disabled="disabled">确认</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { type UploadProps , type UploadUserFile , genFileId ,type UploadRawFile } from 'element-plus';
import myAxios from '@/api/myAxios';
import {ref, onMounted , inject} from 'vue'
import { WarnInfo, SuccessInfo , ErrorInfo, CommInfo } from '@/utils/messageInfo';
const simpleList : any = ref([
{
id: '1', //id
name: '样例1', //
imgUrl: 'https://carbon2.obs.cn-north-4.myhuaweicloud.com:443/feiyi%2Ftest%2F0%2FgGwBQzmm-cxz.png', //
grade: '等级1', //
correspondingGoods: '非遗发簪' //
},
{
id: '2', //id
name: '样例2', //
imgUrl: 'https://carbon2.obs.cn-north-4.myhuaweicloud.com:443/feiyi%2Ftest%2F0%2FgGwBQzmm-cxz.png', //
grade: '等级2', //
correspondingGoods: '非遗螺钿' //
}
])
const goodList = ref([]) //
const DialogVisible = ref(false); //
const disabled = ref(false)
const editForm : any= ref({})
const uploadImg : any = ref() //ref
const carouseImg: any = ref<UploadUserFile[]>([]) //使
const tableData = ref([]) //
const total = ref(0) //
const fileSimple = ref() //
//
const searchParams: any = ref({
//
current: 1,
//
pageSize: 5
})
//
const reload : any = inject("reload")
onMounted(() => {
})
//
const reset =()=>{
reload()
}
const handleSelectionChange = (val:any)=>{
console.log(val)
}
//
const handleSizeChange = (newSize:any) => {
searchParams.value.pageSize = newSize //
//
};
//
const handleCurrentChange = (Current:any) => {
searchParams.value.current = Current //
//
};
//
const onSearch = (data : String)=>{
searchParams.value.name = data
searchParams.value.current = 1
//
}
const deleteCarouse = async (index: number,row: any) =>{ //
simpleList.value.splice(index,1)
}
const editCarouse = async (obj: any) =>{
carouseImg.value.splice(0,carouseImg.value.length)
carouseImg.value.push({ //
url: obj.imgUrl
})
editForm.value = obj
DialogVisible.value = true
}
const saveEdit = ()=>{ //
DialogVisible.value = false
}
const removePic : UploadProps['onRemove'] = (uploadFile, uploadFiles) => { //uploadFileuploadFiles
}
//
const loadForm = (value: any) => {
editForm.value.grade = value
}
const Exceed_ProductImg: UploadProps['onExceed'] = (files) => { // 'onExceed'
WarnInfo('请移除之前的再上传')
}
const handleChange = async (file: any ) => {
fileSimple.value = file
let formData = new FormData() // FormData
formData.append("file", fileSimple.value.raw) //fileSimple.value.raw formData
// const res = await myAxios({ //
// url: '/file/uploadFile',
// method: 'post',
// headers: {
// 'content-Type': 'multipart/form-data'
// },
// data: {
// biz: "test",
// // formDatafile
// file: formData.get("file")
// }
// })
}
</script>
<style scoped>
.headerBg{
background-color: #eee !important;
}
</style>

View File

@ -0,0 +1,215 @@
<template>
<div style="margin-bottom: 10px;">
<el-button type="success" @click="showForm">添加类别</el-button>
</div>
<el-dialog v-model="dialogVisible" title="节日" width="500">
<el-form :model="festivalForm" label-width="auto" style="max-width: 600px" v-loading="loading">
<el-form-item label="添加节日缩略图">
<!-- 下面的event的作用,传入当前事件对象 -->
<el-upload
v-model:file-list="festivalImg"
action="#"
ref="uploadProductImg"
list-type="picture-card"
:auto-upload="false"
multiple="true"
:on-remove="handleRemove"
@change="(event: any) => handleChange(event)"
:on-exceed="Exceed_ProductImg"
limit="1">
<el-icon>
<Plus/>
</el-icon>
</el-upload>
</el-form-item>
<el-form-item label="节日名称">
<!-- <el-input v-model="festivalForm.name" @input="changeInput"/>-->
<el-select v-model="form.festivalName" placeholder="请选择" >
<el-option v-for="item in festivalArr" :key="item" :label="item.name" :value="item.name" />
</el-select>
</el-form-item>
</el-form>
<template #footer #default="{ row, $index }">
<div class="dialog-footer">
<el-button @click="cancel">取消</el-button>
<el-button type="primary" @click="addOrUpdateFestival" :disabled="submitable">
提交
</el-button>
</div>
</template>
</el-dialog>
<!-- 数据展示层 -->
<el-table :data="tableData" border style="width: 100%;" :header-cell-style="{ 'text-align': 'center' }"
@selection-change="handleSelectionChange" :cell-style="{ 'text-align': 'center', 'font-size': '16px' }">
<el-table-column type="selection" width="55"></el-table-column>
<el-table-column prop="url" label="节日图片">
<!-- 插槽拿到父组件的值typeUrl -->
<!-- scope 提供了关于当前行的信息 -->
<template #default="scope"><img :src="scope.row.url" alt="" style="height: 50px;"></template>
</el-table-column>
<el-table-column prop="name" label="节日名称"></el-table-column>
<el-table-column prop="operation" label="操作">
<template #default="scope">
<el-popconfirm class="ml-5" confirm-button-text='确定' cancel-button-text='取消' icon="InfoFilled"
icon-color="red"
title="是否确认删除" @confirm="DeleteFestival(scope.row.name)" width=180>
<template #reference>
<el-button style="height: 25px" class="ml-5" type="danger">删除节日</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
</template>
<script setup lang="ts">
import {ElMessage, type UploadProps, type UploadUserFile,type UploadFile, genFileId, type UploadRawFile} from 'element-plus';
import {ErrorInfo, SuccessInfo, WarnInfo} from '@/utils/messageInfo';
import {onMounted, ref} from 'vue'
import myAxios from '@/api/myAxios';
import {form} from "@/utils/entityProduct/picUpload";
const fileSimple = ref() //
const selectedItems = ref([])
const dialogVisible = ref(false)
const submitable = ref(true) //
const loading = ref(false) //
const festivalImg: any = ref<UploadUserFile[]>([]) //
const uploadProductImg: any = ref() //ref
//
const festivalForm = ref({
id: 0,
name: '',
url: '',
})
//
const tableData = ref([])
// (0) (1)
const flag = ref(0)
const tempImgUrl = ref('') //URL
const festivalArr : any = ref([]) //
//
onMounted(() => {
getFestivalList()
})
//
const getFestivalList = async () => {
const res = await myAxios.get('/festival/get')
if (res.data.code === 1) {
tableData.value = res.data.data
} else {
ElMessage.error('获取类别列表失败,请检查服务')
}
}
//flag
const handleChange = async (file: any) => {
fileSimple.value = file
loading.value = true
let formData = new FormData() // FormData
formData.append("file", fileSimple.value.raw) //fileSimple.value.raw formData
const res = await myAxios({ //
url: '/file/uploadFile',
method: 'post',
headers: {
'content-Type': 'multipart/form-data'
},
data: {
biz: "test",
file: formData.get("file") // formDatafile
}
})
if (res.data.code === 1) {
loading.value = false
SuccessInfo('上传成功')
submitable.value = false
tempImgUrl.value = res.data.data
} else {
ErrorInfo(res.data.message)
}
}
//
const handleRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => {
console.log(uploadFile, uploadFiles)
// typeForm.value.typeUrl = ''
festivalImg.value.splice(0, festivalImg.value.length) //
submitable.value = true //
}
//
const Exceed_ProductImg: UploadProps['onExceed'] = (files) => { // 'onExceed'
uploadProductImg.value!.clearFiles()
const file = files[0] as UploadRawFile
file.uid = genFileId()
uploadProductImg.value!.handleStart(file)
}
//
const showForm = async () => {
resetForm()
tempImgUrl.value = ''
festivalImg.value.splice(0, festivalImg.value.length)
dialogVisible.value = true;
flag.value = 0
}
//
const cancel = () => {
dialogVisible.value = false
resetForm()
}
//
const addOrUpdateFestival = async () => {
//
// if (festivalForm.value.name?.indexOf(' ') !== -1 ||
// festivalForm.value.name == '' || tempImgUrl.value == '') {
// ElMessage({
// type: 'warning',
// message: '/'
// })
// return
// }
//
dialogVisible.value = false;
console.log('添加的类别--->',festivalForm.value)
const res = await myAxios.post('/festival/add', {
name: festivalForm.value.name,
url: '12321234123453124',
//
})
if (res.data.code === 1) {
//
SuccessInfo('添加类别成功')
resetForm()
} else {
ElMessage.error('提交失败')
}
await getFestivalList()
}
//
const resetForm = () => {
festivalForm.value = {
id: 0,
name: '',
url: ''
}
};
const handleSelectionChange = (row: any) => {
selectedItems.value = JSON.parse(JSON.stringify(row))
}
// id
const DeleteFestival = async (name : string) => {
const res = await myAxios.get('/festival/delete', {params:{ festivalName: name}})
if (res.data.code === 1) {
ElMessage({
type: 'success',
message: '删除成功',
})
}
getFestivalList()
}
const changeInput = () => { //
submitable.value = false
}
</script>
<style scoped></style>

View File

@ -1,55 +1,37 @@
<template>
<div class="totalBox">
<div class="fromBox">
<div class="flexCard">
<el-card class="richTextPreview" shadow="never">
<div class="titleText">移动端预览</div>
<p v-html="form.richText"></p>
</el-card>
<!-- 表单 ref prop绑定 用于重置表单 -->
<el-form ref="resetFormData" :model="form" label-width="auto" style="width: 750px;" size="large">
<div class="totalPicture">
<div>
<el-form-item label="添加课程展示图片">
<!-- 下面的event的作用,传入当前事件对象 -->
<el-upload ref="uploadProductImg" action="#" list-type="picture-card" :auto-upload="false" multiple="true"
:on-remove="handleRemove" @change="(event: any) => handleChange(event, 0)"
:on-exceed="Exceed_ProductImg" limit="1">
<el-icon>
<Plus />
</el-icon>
</el-upload>
</el-form-item>
</div>
<!-- 添加图文描述-->
<div>
<el-form-item label="添加课程展示图">
<el-upload ref="uploadProductDetail" action="#" list-type="picture-card" :auto-upload="false"
multiple="true" :on-change="(event: any) => handleChange(event, 1)" :on-exceed="Exceed_ProductDetail"
limit="1" :on-remove="handleRemove">
<el-icon>
<Plus />
</el-icon>
</el-upload>
</el-form-item>
</div>
</div>
<div class="NamePrice">
<div class="name">
<el-form-item label="体验课名称" prop="name">
<el-input v-model="form.name" />
</el-form-item>
</div>
<div class="price">
<el-form-item label="产品价格" prop="price">
<el-input-number v-model="form.price" min="0" :precision="2" :step="0.5" />
<p></p>
</el-form-item>
</div>
</div>
<el-card class="form-FormContainer" shadow="never">
<el-form ref="resetFormData" :model="form" label-width="auto" size="large">
<el-form-item label="添加课程展示图片">
<!-- 下面的event的作用,传入当前事件对象 -->
<el-upload ref="uploadProductImg" action="#" list-type="picture-card" :auto-upload="false" multiple="true"
:on-remove="handleRemove" @change="(event: any) => handleChange(event, 0)"
:on-exceed="Exceed_ProductImg" limit="1">
<el-icon>
<Plus />
</el-icon>
</el-upload>
</el-form-item>
<el-form-item label="体验课名称" prop="name" style="width: 250px">
<el-input v-model="form.name" />
</el-form-item>
<el-form-item label="产品价格" prop="price">
<el-input-number v-model="form.price" min="0" :precision="2" :step="0.5" />
<p></p>
</el-form-item>
<el-form-item label="课程简介" prop="intro">
<el-input v-model="form.intro" type="textarea" style="width: 500px;" />
<el-input v-model="form.intro" type="textarea" style="width: 250px;" />
</el-form-item>
<el-form-item label="商品标签" prop="label">
<el-input v-model="form.label" type="textarea" placeholder="使用英文;分隔符分开" style="width: 500px;" />
<el-input v-model="form.label" type="textarea" placeholder="使用英文;分隔符分开" style="width: 250px;" />
</el-form-item>
<el-form-item label="商品详细描述" prop="introDetail">
<el-input v-model="form.introDetail" type="textarea" placeholder="产品尺寸,服务等" style="width: 500px;" />
<el-form-item>
<richTextUtil @richTextContent="getInfo"></richTextUtil>
</el-form-item>
<div class="totalButton">
<el-form-item>
@ -60,13 +42,12 @@
</el-form-item>
</div>
</el-form>
</div>
<div>
<!-- <appointTime @time-Info="getInfo"></appointTime> -->
</el-card>
<el-card class="form-RichTextContainer" shadow="never">
<calendarUtil @calendar-info="getInfo"></calendarUtil>
</div>
</el-card>
</div>
</template>
<script setup lang="ts">
@ -75,24 +56,22 @@ import { Plus } from '@element-plus/icons-vue';
import { ElMessage, type UploadFile, type UploadProps, genFileId, type UploadRawFile } from 'element-plus';
import myAxios from "@/api/myAxios";
import calendarUtil from '@/layout/components/calendarUtil.vue';
import richTextUtil from '@/layout/components/richTextUtil.vue';
const fileSimple = ref() //
const uploadedFiles = ref<UploadFile[]>([]);//
const uploadedDescription = ref<UploadFile[]>([]);//
const resetFormData = ref()
const form = ref({
name: '',
price: '', //
intro: '',//
label: '',//
introDetail: '',//
goodImg: '', //url
detailImg: '', //url
richText: '',
appointmentDateAddRequestList: [] //
})
//
const reload: any = inject("reload")
const uploadProductImg: any = ref() //ref
const uploadProductDetail: any = ref() //ref
onMounted(() => {
// console.log(myDate.getHours()); //
})
@ -100,7 +79,7 @@ const handleRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => {
console.log(uploadFile, uploadFiles)
}
//
const onSubmit = async () => {
const onSubmit = async () => {
const values = Object.values(form.value);
// 使some()
console.log(form.value.appointmentDateAddRequestList)
@ -112,13 +91,12 @@ const onSubmit = async () => {
return; //
}
console.log(form.value);
const res = await myAxios.post('/goods/add/service', {
const res = await myAxios.post('/goods/add/service', {
name: form.value.name,
price: form.value.price,
goodImg: form.value.goodImg,
intro: form.value.intro,
introDetail: form.value.introDetail,
detailImg: form.value.detailImg,
richTetx: form.value.richText,
label: form.value.label,
appointmentDateAddRequestList: toRaw(form.value.appointmentDateAddRequestList)
})
@ -140,7 +118,7 @@ const handleChange = async (file: any, flag: number) => {
let formData = new FormData() // FormData
formData.append("file", fileSimple.value.raw) //fileSimple.value.raw formData
const res = await myAxios({ //
url: '/file/upload/server/not_login',
url: '/file/uploadFile',
method: 'post',
headers: {
'content-Type': 'multipart/form-data'
@ -153,7 +131,7 @@ const handleChange = async (file: any, flag: number) => {
})
if (res.data.code === 1) {
// flag0 flag1
flag ? form.value.detailImg = res.data.data : form.value.goodImg = res.data.data
form.value.goodImg = res.data.data
}
}
//
@ -166,35 +144,34 @@ const Exceed_ProductImg: UploadProps['onExceed'] = (files) => { //覆盖商品
file.uid = genFileId()
uploadProductImg.value!.handleStart(file)
}
const Exceed_ProductDetail: UploadProps['onExceed'] = (files) => { //
uploadProductDetail.value!.clearFiles()
const file = files[0] as UploadRawFile
file.uid = genFileId()
uploadProductDetail.value!.handleStart(file)
}
const getInfo =(info:any)=>{
console.log('info-->',info);
// timeInfo.value = info
form.value.appointmentDateAddRequestList = info
form.value.richText = info
}
</script>
<style scoped>
.totalBox {
display: flex;
.form-FormContainer {
width: 450px;
/* margin: 0 auto; */
}
.fromBox {
width: 700px;
height: 750px;
.form-RichTextContainer {
width: 550px;
}
.totalPicture {
.flexCard {
display: flex;
height: 100%;
}
.NamePrice {
display: flex;
.richTextPreview {
height: 80vh;
overflow-y: auto;
word-break: break-all;
width: 400px;
margin-right: 10px;
}
.totalButton {
@ -202,32 +179,5 @@ const getInfo =(info:any)=>{
justify-content: space-evenly;
/* 盒子里面水平分隔开 */
}
.midBox {
display: flex;
}
.date-picker {
flex: 0.3;
/* 日期选择区域占据左边 */
display: inline-block;
margin-bottom: 10px;
}
.father {
display: flex;
justify-content: space-around;
align-items: center;
border: 1px solid red;
}
.box {
margin-bottom: 20px;
/* 每个日期或时间段之间的间距 */
border: 1px solid yellow;
}
.inner {
margin-bottom: 10px;
border: 1px solid orange;
}
</style>

View File

@ -232,7 +232,9 @@ import {ref, onMounted, inject, toRaw} from 'vue';
import myAxios from "@/api/myAxios";
import calendarUtil from '@/layout/components/calendarUtil.vue';
import emitter from "@/utils/emitter";
import {form} from "@/utils/entityProduct/picUpload";
import { useRouter } from "vue-router";
const router = useRouter(); //
const total = ref(0); //
const idList = ref<Number[]>([]); //
const searchParams: any = ref({ //
@ -347,9 +349,15 @@ const deleteProduct = async (index: number) => {
}
//
const ReviseOrView = (index: number, row: any , flag : number) => {
router.push({
name: '课程详情',
params: {
id: row.id,
flag: flag
}
})
//flag disabled
flag ? (title.value = "编辑商品" , disabled.value = false) : (title.value = "商品详情" , disabled.value = true)
DialogVisible.value = true;
editForm.value = JSON.parse(JSON.stringify(row));
};
//()
@ -400,7 +408,7 @@ const handleChange = async (file: any, flag: number) => {
let formData = new FormData() // FormData
formData.append("file", fileSimple.value.raw) //fileSimple.value.raw formData
const res = await myAxios({ //
url: '/file/upload/server/not_login',
url: '/file/uploadFile',
method: 'post',
headers: {
'content-Type': 'multipart/form-data'
@ -413,6 +421,7 @@ const handleChange = async (file: any, flag: number) => {
if (res.data.code === 1) {
// flag0 flag1
flag ? editForm.value.detailImg = res.data.data : editForm.value.goodImg = res.data.data
console.log('res--->',res.data.data)
}
}
const onSearch = (data : String)=>{ //

View File

@ -0,0 +1,189 @@
<template>
<div class="flexCard">
<el-card class="richTextPreview" shadow="never">
<div class="titleText">移动端预览</div>
<p v-html="detailFrom.richText"></p>
</el-card>
<!-- 表单 ref prop绑定 用于重置表单 -->
<el-card class="form-container" shadow="never">
<div class="form">
<el-form ref="resetFormData" :model="detailFrom" label-width="auto" size="large" :rules="rules" :disabled="true">
<div>
<el-form-item label="添加商品图片">
<!-- 下面的event的作用,传入当前事件对象 -->
<el-upload v-model:file-list="ImgArr" ref="uploadProductImg" action="#" list-type="picture-card" :auto-upload="false" multiple="true"
:on-remove="handleRemove" @change="(event: any) => handleChange(event, 2)" :on-exceed="Exceed_ProductImg"
limit="7">
<el-icon>
<Plus />
</el-icon>
</el-upload>
</el-form-item>
</div>
<div class="NamePrice">
<el-form-item label="课程名称" prop="name">
<el-input v-model="detailFrom.name" maxlength="12" minlength="2" show-word-limit style="width: 260px;" />
</el-form-item>
<el-form-item label="单次价格" prop="price">
<el-input-number v-model="detailFrom.price" min="0" :precision="2" :step="0.5" />
<p></p>
</el-form-item>
</div>
<div class="IntroInven">
<el-form-item label="课程简介" prop="intro">
<el-input v-model="detailFrom.intro" type="textarea" placeholder="产品尺寸,服务等" maxlength="30" show-word-limit
style="width: 300px;" />
</el-form-item>
</div>
<el-form-item label="课程详情图" prop="richText">
<richTextUtil @richTextContent="getInfo" v-model:disableRich="disabledRichText"/>
</el-form-item>
<div class="btnBox">
<el-form-item>
<el-button type="primary" @click="onSubmit" :disabled="saveBtn">更新</el-button>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="resetForm">重置</el-button>
</el-form-item>
</div>
</el-form>
</div>
</el-card>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted, inject, reactive , onBeforeMount} from 'vue';
import { Plus } from '@element-plus/icons-vue';
import {type FormRules, type UploadUserFile} from 'element-plus';
import { SuccessInfo, WarnInfo, CommInfo } from '@/utils/messageInfo'; //ElMessage
import myAxios from "@/api/myAxios";
import { saveBtn, invInput } from '@/utils/entityProduct/globalVar';
import { validateName, validateIntro, validateDetail } from '@/utils/entityProduct/FormRules';
import { handleChange, handleRemove, form, Exceed_ProductImg } from '@/utils/entityProduct/picUpload';
import richTextUtil from '@/layout/components/richTextUtil.vue';
import { useRoute } from "vue-router";
const route = useRoute(); //
const resetFormData = ref()
const reload: any = inject("reload") //
const uploadProductImg: any = ref() //ref
const ImgArr: any = ref<UploadUserFile[]>([])
const detailFrom : any = ref({
name: '', //
price: '', //
intro: '',//
festivalName: '', //
type: '',//
label: '',//
goodImg: '', //url
inventory: '', //
richText: ''
})
const disabledRichText:any = ref(false);
onBeforeMount(()=>{
if(route.params.flag === '0'){
disabledRichText.value = true;
}
})
onMounted(() => {
getServiceInfo() //
// console.log('id---->',route.params.id)
// console.log('flag---->',route.params.flag)
})
const getServiceInfo = async () => {
const res = await myAxios.post("/goods/service/list/id",{ id: route.params.id })
console.log('res--->',res.data)
if(res.data.code == 1){
detailFrom.value = res.data.data
ImgArr.value.splice(0,ImgArr.value.length)
var tempArr = res.data.data.goodImg.split(';')
tempArr.forEach((item: any) => {
ImgArr.value.push({
url: item
})
})
} else {
WarnInfo('信息获取失败')
}
}
//
const onSubmit = async () => {
const values = Object.values(detailFrom.value );
// 使some()
if (values.some((value: any) => value === null || value === undefined || value === '' || value === 0)) {
WarnInfo('请检查表单数据是否完整填写')
return; //
}
const res = await myAxios.post('/goods/add', { ...detailFrom.value })
// console.log(res.data);
if (res.data.code === 1) {
SuccessInfo('提交成功')
detailFrom.value = {} //12.18
reload() //
} else {
WarnInfo('服务错误')
return;
}
}
//
const resetForm = () => {
resetFormData.value.resetFields()
}
const rules = reactive<FormRules<typeof form>>({ //
name: [{ validator: validateName, trigger: 'blur' }],
intro: [{ validator: validateIntro, trigger: 'blur' }],
introDetail: [{ validator: validateDetail, trigger: 'blur' }]
})
const getInfo = (info: any) => { //
console.log('info', info)
detailFrom.value.richText = info
}
</script>
<style scoped>
.flexCard {
display: flex;
height: 100%;
}
.richTextPreview {
height: 80vh;
overflow-y: auto;
word-break: break-all;
width: 400px;
margin-right: 10px;
}
.form-container {
width: 700px;
/* margin: 0 auto; */
}
.form {
/* width: 500px; */
margin: 0 auto;
}
.NamePrice {
display: flex;
}
.IntroInven {
display: flex;
}
.festivalBox {
display: flex;
}
.btnBox {
display: flex;
justify-content: space-around;
}
</style>

View File

@ -81,7 +81,7 @@ const handleChange = async (file: any) => {
let formData = new FormData() // FormData
formData.append("file", fileSimple.value.raw) //fileSimple.value.raw formData
const res = await myAxios({ //
url: '/file/upload/server/not_login',
url: '/file/uploadFile',
method: 'post',
headers: {
'content-Type': 'multipart/form-data'

View File

@ -32,7 +32,7 @@ const handleChange = async (file: any, flag: number) => {
let formData = new FormData() // FormData
formData.append("file", fileSimple.value.raw) //fileSimple.value.raw formData
const res = await myAxios({ //
url: '/file/upload/server/not_login',
url: '/file/uploadFile',
method: 'post',
headers: {
'content-Type': 'multipart/form-data'

View File

@ -117,7 +117,7 @@ const handleChange = async (file: any) => {
let formData = new FormData() // FormData
formData.append("file", fileSimple.value.raw) //fileSimple.value.raw formData
const res = await myAxios({ //
url: '/file/upload/server/not_login',
url: '/file/uploadFile',
method: 'post',
headers: {
'content-Type': 'multipart/form-data'

View File

@ -1,28 +1,128 @@
<template>
<el-scrollbar height="100px">
<H2 v-for="item in 1" :key="item" class="scrollbar-demo-item">
Scrollbar 滚动条内容{{ item }}:<br /><br />
逆境清醒<br />
阳光总在风雨后<br />
历练中完成自我升华!<br />
共勉<br />
</H2>
</el-scrollbar>
<el-container class="layout-container-demo" style="height: 500px">
<el-aside width="200px">
<el-scrollbar>
<el-menu :default-openeds="['1', '3']">
<el-sub-menu index="1">
<template #title>
<el-icon><message /></el-icon>Navigator One
</template>
<el-menu-item-group>
<template #title>Group 1</template>
<el-menu-item index="1-1">Option 1</el-menu-item>
<el-menu-item index="1-2">Option 2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="Group 2">
<el-menu-item index="1-3">Option 3</el-menu-item>
</el-menu-item-group>
<el-sub-menu index="1-4">
<template #title>Option4</template>
<el-menu-item index="1-4-1">Option 4-1</el-menu-item>
</el-sub-menu>
</el-sub-menu>
<el-sub-menu index="2">
<template #title>
<el-icon><icon-menu /></el-icon>Navigator Two
</template>
<el-menu-item-group>
<template #title>Group 1</template>
<el-menu-item index="2-1">Option 1</el-menu-item>
<el-menu-item index="2-2">Option 2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="Group 2">
<el-menu-item index="2-3">Option 3</el-menu-item>
</el-menu-item-group>
<el-sub-menu index="2-4">
<template #title>Option 4</template>
<el-menu-item index="2-4-1">Option 4-1</el-menu-item>
</el-sub-menu>
</el-sub-menu>
<el-sub-menu index="3">
<template #title>
<el-icon><setting /></el-icon>Navigator Three
</template>
<el-menu-item-group>
<template #title>Group 1</template>
<el-menu-item index="3-1">Option 1</el-menu-item>
<el-menu-item index="3-2">Option 2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="Group 2">
<el-menu-item index="3-3">Option 3</el-menu-item>
</el-menu-item-group>
<el-sub-menu index="3-4">
<template #title>Option 4</template>
<el-menu-item index="3-4-1">Option 4-1</el-menu-item>
</el-sub-menu>
</el-sub-menu>
</el-menu>
</el-scrollbar>
</el-aside>
<el-container>
<el-header style="text-align: right; font-size: 12px">
<div class="toolbar">
<el-dropdown>
<el-icon style="margin-right: 8px; margin-top: 1px">
<setting />
</el-icon>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>View</el-dropdown-item>
<el-dropdown-item>Add</el-dropdown-item>
<el-dropdown-item>Delete</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<span>Tom</span>
</div>
</el-header>
<el-main>
<el-scrollbar>
<el-table :data="tableData">
<el-table-column prop="date" label="Date" width="140" />
<el-table-column prop="name" label="Name" width="120" />
<el-table-column prop="address" label="Address" />
</el-table>
</el-scrollbar>
</el-main>
</el-container>
</el-container>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { Menu as IconMenu, Message, Setting } from '@element-plus/icons-vue'
const item = {
date: '2016-05-02',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
}
const tableData = ref(Array.from({ length: 20 }).fill(item))
</script>
<style scoped>
.scrollbar-demo-item {
display: flex;
align-items: center;
justify-content: center;
height: 400px;
margin: 10px;
padding:30px;
width:300px;
text-align: left;
border-radius: 4px;
background: var(--el-color-primary-light-9);
color: var(--el-color-primary);
border:5px solid #0094ff;
}
.layout-container-demo .el-header {
position: relative;
background-color: var(--el-color-primary-light-7);
color: var(--el-text-color-primary);
}
.layout-container-demo .el-aside {
color: var(--el-text-color-primary);
background: var(--el-color-primary-light-8);
}
.layout-container-demo .el-menu {
border-right: none;
}
.layout-container-demo .el-main {
padding: 0;
}
.layout-container-demo .toolbar {
display: inline-flex;
align-items: center;
justify-content: center;
height: 100%;
right: 20px;
}
</style>