This commit is contained in:
许gitee 2024-12-24 10:16:37 +08:00
parent c4989feb43
commit 6c83e24432
61 changed files with 18843 additions and 0 deletions

23
.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

5
babel.config.js Normal file
View File

@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}

19
jsconfig.json Normal file
View File

@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "es5",
"module": "esnext",
"baseUrl": "./",
"moduleResolution": "node",
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
}
}

13087
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

39
package.json Normal file
View File

@ -0,0 +1,39 @@
{
"name": "xhysalesweb",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build"
},
"dependencies": {
"@infectoone/vue-ganttastic": "^2.3.2",
"@pureadmin/utils": "^2.5.0",
"axios": "^1.7.7",
"core-js": "^3.8.3",
"echarts": "^5.5.1",
"element-plus": "^2.8.3",
"sass": "^1.79.2",
"sass-loader": "^16.0.1",
"svg-sprite-loader": "^6.0.11",
"vue": "^3.2.13",
"vue-router": "^4.0.3",
"vue3-count-to": "^1.1.2",
"vuex": "^4.0.0"
},
"devDependencies": {
"@iconify-icons/ep": "^1.2.12",
"@iconify/icons-ri": "^1.2.10",
"@iconify/vue": "^4.2.0",
"@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-plugin-router": "~5.0.0",
"@vue/cli-plugin-vuex": "~5.0.0",
"@vue/cli-service": "~5.0.0"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead",
"not ie 11"
]
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

17
public/index.html Normal file
View File

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

9
src/App.vue Normal file
View File

@ -0,0 +1,9 @@
<template>
<router-view/>
</template>
<style>
html,body,#app{
height: 100%;
}
</style>

BIN
src/assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -0,0 +1,182 @@
@charset "utf-8";
.border,
.border-top,
.border-right,
.border-bottom,
.border-left,
.border-topbottom,
.border-rightleft,
.border-topleft,
.border-rightbottom,
.border-topright,
.border-bottomleft {
position: relative;
}
.border::before,
.border-top::before,
.border-right::before,
.border-bottom::before,
.border-left::before,
.border-topbottom::before,
.border-topbottom::after,
.border-rightleft::before,
.border-rightleft::after,
.border-topleft::before,
.border-topleft::after,
.border-rightbottom::before,
.border-rightbottom::after,
.border-topright::before,
.border-topright::after,
.border-bottomleft::before,
.border-bottomleft::after {
content: "\0020";
overflow: hidden;
position: absolute;
}
/* border
* 边框是由伪元素区域遮盖在父级
* 子级若有交互需要对子级设置
* 定位 z轴
*/
.border::before {
box-sizing: border-box;
top: 0;
left: 0;
height: 100%;
width: 100%;
border: 1px solid #eaeaea;
transform-origin: 0 0;
}
.border-top::before,
.border-bottom::before,
.border-topbottom::before,
.border-topbottom::after,
.border-topleft::before,
.border-rightbottom::after,
.border-topright::before,
.border-bottomleft::before {
left: 0;
width: 100%;
height: 1px;
}
.border-right::before,
.border-left::before,
.border-rightleft::before,
.border-rightleft::after,
.border-topleft::after,
.border-rightbottom::before,
.border-topright::after,
.border-bottomleft::after {
top: 0;
width: 1px;
height: 100%;
}
.border-top::before,
.border-topbottom::before,
.border-topleft::before,
.border-topright::before {
border-top: 1px solid #eaeaea;
transform-origin: 0 0;
}
.border-right::before,
.border-rightbottom::before,
.border-rightleft::before,
.border-topright::after {
border-right: 1px solid #eaeaea;
transform-origin: 100% 0;
}
.border-bottom::before,
.border-topbottom::after,
.border-rightbottom::after,
.border-bottomleft::before {
border-bottom: 1px solid #eaeaea;
transform-origin: 0 100%;
}
.border-left::before,
.border-topleft::after,
.border-rightleft::after,
.border-bottomleft::after {
border-left: 1px solid #eaeaea;
transform-origin: 0 0;
}
.border-top::before,
.border-topbottom::before,
.border-topleft::before,
.border-topright::before {
top: 0;
}
.border-right::before,
.border-rightleft::after,
.border-rightbottom::before,
.border-topright::after {
right: 0;
}
.border-bottom::before,
.border-topbottom::after,
.border-rightbottom::after,
.border-bottomleft::after {
bottom: 0;
}
.border-left::before,
.border-rightleft::before,
.border-topleft::after,
.border-bottomleft::before {
left: 0;
}
@media (max--moz-device-pixel-ratio: 1.49), (-webkit-max-device-pixel-ratio: 1.49), (max-device-pixel-ratio: 1.49), (max-resolution: 143dpi), (max-resolution: 1.49dppx) {
/* 默认值,无需重置 */
}
@media (min--moz-device-pixel-ratio: 1.5) and (max--moz-device-pixel-ratio: 2.49), (-webkit-min-device-pixel-ratio: 1.5) and (-webkit-max-device-pixel-ratio: 2.49), (min-device-pixel-ratio: 1.5) and (max-device-pixel-ratio: 2.49), (min-resolution: 144dpi) and (max-resolution: 239dpi), (min-resolution: 1.5dppx) and (max-resolution: 2.49dppx) {
.border::before {
width: 200%;
height: 200%;
transform: scale(.5);
}
.border-top::before,
.border-bottom::before,
.border-topbottom::before,
.border-topbottom::after,
.border-topleft::before,
.border-rightbottom::after,
.border-topright::before,
.border-bottomleft::before {
transform: scaleY(.5);
}
.border-right::before,
.border-left::before,
.border-rightleft::before,
.border-rightleft::after,
.border-topleft::after,
.border-rightbottom::before,
.border-topright::after,
.border-bottomleft::after {
transform: scaleX(.5);
}
}
@media (min--moz-device-pixel-ratio: 2.5), (-webkit-min-device-pixel-ratio: 2.5), (min-device-pixel-ratio: 2.5), (min-resolution: 240dpi), (min-resolution: 2.5dppx) {
.border::before {
width: 300%;
height: 300%;
transform: scale(.33333);
}
.border-top::before,
.border-bottom::before,
.border-topbottom::before,
.border-topbottom::after,
.border-topleft::before,
.border-rightbottom::after,
.border-topright::before,
.border-bottomleft::before {
transform: scaleY(.33333);
}
.border-right::before,
.border-left::before,
.border-rightleft::before,
.border-rightleft::after,
.border-topleft::after,
.border-rightbottom::before,
.border-topright::after,
.border-bottomleft::after {
transform: scaleX(.33333);
}
}

View File

@ -0,0 +1,26 @@
@charset "utf-8";
html{background-color:#fff;color:#000;font-size:12px}
body,ul,ol,dl,dd,h1,h2,h3,h4,h5,h6,figure,form,fieldset,legend,input,textarea,button,p,blockquote,th,td,pre,xmp{margin:0;padding:0}
body,input,textarea,button,select,pre,xmp,tt,code,kbd,samp{line-height:1.5;font-family:tahoma,arial,"Hiragino Sans GB",simsun,sans-serif}
h1,h2,h3,h4,h5,h6,small,big,input,textarea,button,select{font-size:100%}
h1,h2,h3,h4,h5,h6{font-family:tahoma,arial,"Hiragino Sans GB","微软雅黑",simsun,sans-serif}
h1,h2,h3,h4,h5,h6,b,strong{font-weight:normal}
address,cite,dfn,em,i,optgroup,var{font-style:normal}
table{border-collapse:collapse;border-spacing:0;text-align:left}
caption,th{text-align:inherit}
ul,ol,menu{list-style:none}
fieldset,img{border:0}
img,object,input,textarea,button,select{vertical-align:middle}
article,aside,footer,header,section,nav,figure,figcaption,hgroup,details,menu{display:block}
audio,canvas,video{display:inline-block;*display:inline;*zoom:1}
blockquote:before,blockquote:after,q:before,q:after{content:"\0020"}
textarea{overflow:auto;resize:vertical}
input,textarea,button,select,a{outline:0 none;border: none;}
button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}
mark{background-color:transparent}
a,ins,s,u,del{text-decoration:none}
sup,sub{vertical-align:baseline}
html {overflow-x: hidden;height: 100%;font-size: 50px;-webkit-tap-highlight-color: transparent;}
body {font-family: Arial, "Microsoft Yahei", "Helvetica Neue", Helvetica, sans-serif;color: #333;font-size: .28em;line-height: 1;-webkit-text-size-adjust: none;}
hr {height: .02rem;margin: .1rem 0;border: medium none;border-top: .02rem solid #cacaca;}
a {color: #25a4bb;text-decoration: none;}

View File

@ -0,0 +1,27 @@
<template>
<svg class="svg-icon" aria-hidden="true">
<use :xlink:href="iconName"></use>
</svg>
</template>
<script setup>
import {defineProps, computed} from 'vue'
const props = defineProps({
icon: {
type: String,
required: true
}
})
const iconName = computed(() => {
return `#icon-${props.icon}`
})
</script>
<style lang="scss" scoped>
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
</style>

8
src/icons/index.js Normal file
View File

@ -0,0 +1,8 @@
import SvgIcon from '@/components/SvgIcon'
const svgRequired = require.context('./svg', false, /\.svg$/)
svgRequired.keys().forEach((item) => svgRequired(item))
export default (app) => {
app.component('svg-icon', SvgIcon)
}

View File

@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M49.217 41.329l-.136-35.24c-.06-2.715-2.302-4.345-5.022-4.405h-3.65c-2.712-.06-4.866 2.303-4.806 5.016l.152 19.164-24.151-23.79a6.698 6.698 0 0 0-9.499 0 6.76 6.76 0 0 0 0 9.526l23.93 23.713-18.345.074c-2.712-.069-5.228 1.813-5.64 5.02v3.462c.069 2.721 2.31 4.97 5.022 5.03l35.028-.207c.052.005.087.025.133.025l2.457.054a4.626 4.626 0 0 0 3.436-1.38c.88-.874 1.205-2.096 1.169-3.462l-.262-2.465c0-.048.182-.081.182-.136h.002zm52.523 51.212l18.32-.073c2.713.06 5.224-1.609 5.64-4.815v-3.462c-.068-2.722-2.317-4.97-5.021-5.04l-34.58.21c-.053 0-.086-.021-.138-.021l-2.451-.06a4.64 4.64 0 0 0-3.445 1.381c-.885.868-1.201 2.094-1.174 3.46l.27 2.46c.005.06-.177.095-.177.141l.141 34.697c.069 2.713 2.31 4.338 5.022 4.397l3.45.006c2.705.062 4.867-2.31 4.8-5.026l-.153-18.752 24.151 23.946a6.69 6.69 0 0 0 9.494 0 6.747 6.747 0 0 0 0-9.523L101.74 92.54v.001zM48.125 80.662a4.636 4.636 0 0 0-3.437-1.382l-2.457.06c-.05 0-.082.022-.137.022l-35.025-.21c-2.712.07-4.957 2.318-5.022 5.04v3.462c.409 3.206 2.925 4.874 5.633 4.814l18.554.06-24.132 23.928c-2.62 2.626-2.62 6.89 0 9.524a6.694 6.694 0 0 0 9.496 0l24.155-23.79-.155 18.866c-.06 2.722 2.094 5.093 4.801 5.025h3.65c2.72-.069 4.962-1.685 5.022-4.406l.141-34.956c0-.05-.182-.082-.182-.136l.262-2.46c.03-1.366-.286-2.592-1.166-3.46h-.001zM80.08 47.397a4.62 4.62 0 0 0 3.443 1.374l2.45-.054c.055 0 .088-.02.143-.028l35.08.21c2.712-.062 4.953-2.312 5.021-5.033l.009-3.463c-.417-3.211-2.937-5.084-5.64-5.025l-18.615-.073 23.917-23.715c2.63-2.623 2.63-6.879.008-9.513a6.691 6.691 0 0 0-9.494 0L92.251 26.016l.155-19.312c.065-2.713-2.097-5.085-4.802-5.025h-3.45c-2.713.069-4.954 1.693-5.022 4.406l-.139 35.247c0 .054.18.088.18.136l-.267 2.465c-.028 1.366.288 2.588 1.174 3.463v.001z"/></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="128" height="128"><defs><style/></defs><path d="M512 128q69.675 0 135.51 21.163t115.498 54.997 93.483 74.837 73.685 82.006 51.67 74.837 32.17 54.827L1024 512q-2.347 4.992-6.315 13.483T998.87 560.17t-31.658 51.669-44.331 59.99-56.832 64.34-69.504 60.16-82.347 51.5-94.848 34.687T512 896q-69.675 0-135.51-21.163t-115.498-54.826-93.483-74.326-73.685-81.493-51.67-74.496-32.17-54.997L0 513.707q2.347-4.992 6.315-13.483t18.816-34.816 31.658-51.84 44.331-60.33 56.832-64.683 69.504-60.331 82.347-51.84 94.848-34.816T512 128.085zm0 85.333q-46.677 0-91.648 12.331t-81.152 31.83-70.656 47.146-59.648 54.485-48.853 57.686-37.675 52.821-26.325 43.99q12.33 21.674 26.325 43.52t37.675 52.351 48.853 57.003 59.648 53.845T339.2 767.02t81.152 31.488T512 810.667t91.648-12.331 81.152-31.659 70.656-46.848 59.648-54.186 48.853-57.344 37.675-52.651T927.957 512q-12.33-21.675-26.325-43.648t-37.675-52.65-48.853-57.345-59.648-54.186-70.656-46.848-81.152-31.659T512 213.334zm0 128q70.656 0 120.661 50.006T682.667 512 632.66 632.661 512 682.667 391.339 632.66 341.333 512t50.006-120.661T512 341.333zm0 85.334q-35.328 0-60.33 25.002T426.666 512t25.002 60.33T512 597.334t60.33-25.002T597.334 512t-25.002-60.33T512 426.666z"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

1
src/icons/svg/eye.svg Normal file
View File

@ -0,0 +1 @@
<svg width="128" height="64" xmlns="http://www.w3.org/2000/svg"><path d="M127.072 7.994c1.37-2.208.914-5.152-.914-6.87-2.056-1.717-4.797-1.226-6.396.982-.229.245-25.586 32.382-55.74 32.382-29.24 0-55.74-32.382-55.968-32.627-1.6-1.963-4.57-2.208-6.397-.49C-.17 3.086-.399 6.275 1.2 8.238c.457.736 5.94 7.36 14.62 14.72L4.17 35.96c-1.828 1.963-1.6 5.152.228 6.87.457.98 1.6 1.471 2.742 1.471s2.284-.49 3.198-1.472l12.564-13.983c5.94 4.416 13.021 8.587 20.788 11.53l-4.797 17.418c-.685 2.699.686 5.397 3.198 6.133h1.37c2.057 0 3.884-1.472 4.341-3.68L52.6 42.83c3.655.736 7.538 1.227 11.422 1.227 3.883 0 7.767-.49 11.422-1.227l4.797 17.173c.457 2.208 2.513 3.68 4.34 3.68.457 0 .914 0 1.143-.246 2.513-.736 3.883-3.434 3.198-6.133l-4.797-17.172c7.767-2.944 14.848-7.114 20.788-11.53l12.336 13.738c.913.981 2.056 1.472 3.198 1.472s2.284-.49 3.198-1.472c1.828-1.963 1.828-4.906.228-6.87l-11.65-13.001c9.366-7.36 14.849-14.474 14.849-14.474z"/></svg>

After

Width:  |  Height:  |  Size: 944 B

View File

@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M38.47 52L52 38.462l-23.648-23.67L43.209 0H.035L0 43.137l14.757-14.865L38.47 52zm74.773 47.726L89.526 76 76 89.536l23.648 23.672L84.795 128h43.174L128 84.863l-14.757 14.863zM89.538 52l23.668-23.648L128 43.207V.038L84.866 0 99.73 14.76 76 38.472 89.538 52zM38.46 76L14.792 99.651 0 84.794v43.173l43.137.033-14.865-14.757L52 89.53 38.46 76z"/></svg>

After

Width:  |  Height:  |  Size: 421 B

1
src/icons/svg/guide.svg Normal file
View File

@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M1.482 70.131l36.204 16.18 69.932-65.485-61.38 70.594 46.435 18.735c1.119.425 2.397-.17 2.797-1.363v-.085L127.998.047 1.322 65.874c-1.12.597-1.519 1.959-1.04 3.151.32.511.72.937 1.2 1.107zm44.676 57.821L64.22 107.26l-18.062-7.834v28.527z"/></svg>

After

Width:  |  Height:  |  Size: 320 B

View File

@ -0,0 +1 @@
<svg t="1631523205707" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1504" width="128" height="128"><path d="M393.67111111 432.35555555h546.13333334a9.10222222 9.10222222 0 0 0 9.10222222-9.10222222v-63.71555555a9.10222222 9.10222222 0 0 0-9.10222222-9.10222223H393.67111111a9.10222222 9.10222222 0 0 0-9.10222222 9.10222223v63.71555555a9.10222222 9.10222222 0 0 0 9.10222222 9.10222222z m-9.10222222 232.10666667a9.10222222 9.10222222 0 0 0 9.10222222 9.10222223h546.13333334a9.10222222 9.10222222 0 0 0 9.10222222-9.10222223v-63.71555555a9.10222222 9.10222222 0 0 0-9.10222222-9.10222222H393.67111111a9.10222222 9.10222222 0 0 0-9.10222222 9.10222222v63.71555555z m573.44-552.96H65.99111111a9.10222222 9.10222222 0 0 0-9.10222222 9.10222223v63.71555555a9.10222222 9.10222222 0 0 0 9.10222222 9.10222222h892.01777778a9.10222222 9.10222222 0 0 0 9.10222222-9.10222222v-63.71555555a9.10222222 9.10222222 0 0 0-9.10222222-9.10222223z m0 719.07555556H65.99111111a9.10222222 9.10222222 0 0 0-9.10222222 9.10222222v63.71555555a9.10222222 9.10222222 0 0 0 9.10222222 9.10222223h892.01777778a9.10222222 9.10222222 0 0 0 9.10222222-9.10222223v-63.71555555a9.10222222 9.10222222 0 0 0-9.10222222-9.10222222zM91.47733333 660.02488889L269.312 519.96444445a10.01244445 10.01244445 0 0 0 0-15.81511112L91.47733333 363.97511111a10.12622222 10.12622222 0 0 0-16.384 7.85066667v280.23466667a10.12622222 10.12622222 0 0 0 16.384 7.96444444z" p-id="1505"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1 @@
<svg t="1631523178203" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1362" width="128" height="128"><path d="M630.32888889 591.64444445l-546.13333334 0a9.10222222 9.10222222 0 0 0-9.10222222 9.10222222l0 63.71555555a9.10222222 9.10222222 0 0 0 9.10222222 9.10222223L630.32888889 673.56444444a9.10222222 9.10222222 0 0 0 9.10222222-9.10222222l0-63.71555555a9.10222222 9.10222222 0 0 0-9.10222222-9.10222222z m9.10222222-232.10666667a9.10222222 9.10222222 0 0 0-9.10222222-9.10222222l-546.13333334-1e-8a9.10222222 9.10222222 0 0 0-9.10222222 9.10222223l0 63.71555555a9.10222222 9.10222222 0 0 0 9.10222222 9.10222222L630.32888889 432.35555555a9.10222222 9.10222222 0 0 0 9.10222222-9.10222222l0-63.71555555z m-573.44 552.96L958.00888889 912.49777778a9.10222222 9.10222222 0 0 0 9.10222221-9.10222223l1e-8-63.71555555a9.10222222 9.10222222 0 0 0-9.10222222-9.10222222l-892.01777778 0a9.10222222 9.10222222 0 0 0-9.10222222 9.10222222l1e-8 63.71555555a9.10222222 9.10222222 0 0 0 9.10222221 9.10222223z m0-719.07555556L958.00888889 193.42222222a9.10222222 9.10222222 0 0 0 9.10222222-9.10222222l-1e-8-63.71555555a9.10222222 9.10222222 0 0 0-9.10222221-9.10222223l-892.01777778 0a9.10222222 9.10222222 0 0 0-9.10222221 9.10222223l-1e-8 63.71555555a9.10222222 9.10222222 0 0 0 9.10222222 9.10222222zM932.52266667 363.97511111L754.688 504.03555555a10.01244445 10.01244445 0 0 0 0 15.81511112L932.52266667 660.02488889a10.12622222 10.12622222 0 0 0 16.384-7.85066667l0-280.23466667a10.12622222 10.12622222 0 0 0-16.384-7.96444444z" p-id="1363"></path></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M84.742 36.8c2.398 7.2 5.595 12.8 11.19 18.4 4.795-4.8 7.992-11.2 10.39-18.4h-21.58zm-52.748 40h20.78l-10.39-28-10.39 28z"/><path d="M111.916 0H16.009C7.218 0 .025 7.2.025 16v96c0 8.8 7.193 16 15.984 16h95.907c8.791 0 15.984-7.2 15.984-16V16c0-8.8-6.394-16-15.984-16zM72.754 103.2c-1.598 1.6-3.197 1.6-4.795 1.6-.8 0-2.398 0-3.197-.8-.8-.8-1.599 0-1.599-.8s-.799-1.6-1.598-3.2c-.8-1.6-.8-2.4-1.599-4l-3.196-8.8H28.797L25.6 96c-1.598 3.2-2.398 5.6-3.197 7.2-.8 1.6-2.398 1.6-4.795 1.6-1.599 0-3.197-.8-4.796-1.6-1.598-1.6-2.397-2.4-2.397-4 0-.8 0-1.6.799-3.2.8-1.6.8-2.4 1.598-4l17.583-44.8c.8-1.6.8-3.2 1.599-4.8.799-1.6 1.598-3.2 2.397-4 .8-.8 1.599-2.4 3.197-3.2 1.599-.8 3.197-.8 4.796-.8 1.598 0 3.196 0 4.795.8 1.598.8 2.398 1.6 3.197 3.2.799.8 1.598 2.4 2.397 4 .8 1.6 1.599 3.2 2.398 5.6l17.583 44c1.598 3.2 2.398 5.6 2.398 7.2-.8.8-1.599 2.4-2.398 4zM116.711 72c-8.791-3.2-15.185-7.2-20.78-12-5.594 5.6-12.787 9.6-21.579 12l-2.397-4c8.791-2.4 15.984-5.6 21.579-11.2C87.939 51.2 83.144 44 81.545 36h-7.992v-3.2h21.58c-1.6-2.4-3.198-5.6-4.796-8l2.397-.8c1.599 2.4 3.997 5.6 5.595 8.8h19.98v4h-7.992c-2.397 8-6.393 15.2-11.189 20 5.595 4.8 11.988 8.8 20.78 11.2l-3.197 4z"/></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M108.8 44.322H89.6v-5.36c0-9.04-3.308-24.163-25.6-24.163-23.145 0-25.6 16.881-25.6 24.162v5.361H19.2v-5.36C19.2 15.281 36.798 0 64 0c27.202 0 44.8 15.281 44.8 38.961v5.361zm-32 39.356c0-5.44-5.763-9.832-12.8-9.832-7.037 0-12.8 4.392-12.8 9.832 0 3.682 2.567 6.808 6.407 8.477v11.205c0 2.718 2.875 4.962 6.4 4.962 3.524 0 6.4-2.244 6.4-4.962V92.155c3.833-1.669 6.393-4.795 6.393-8.477zM128 64v49.201c0 8.158-8.645 14.799-19.2 14.799H19.2C8.651 128 0 121.359 0 113.201V64c0-8.153 8.645-14.799 19.2-14.799h89.6c10.555 0 19.2 6.646 19.2 14.799z"/></svg>

After

Width:  |  Height:  |  Size: 623 B

1
src/icons/svg/user.svg Normal file
View File

@ -0,0 +1 @@
<svg width="130" height="130" xmlns="http://www.w3.org/2000/svg"><path d="M63.444 64.996c20.633 0 37.359-14.308 37.359-31.953 0-17.649-16.726-31.952-37.359-31.952-20.631 0-37.36 14.303-37.358 31.952 0 17.645 16.727 31.953 37.359 31.953zM80.57 75.65H49.434c-26.652 0-48.26 18.477-48.26 41.27v2.664c0 9.316 21.608 9.325 48.26 9.325H80.57c26.649 0 48.256-.344 48.256-9.325v-2.663c0-22.794-21.605-41.271-48.256-41.271z" stroke="#979797"/></svg>

After

Width:  |  Height:  |  Size: 440 B

17
src/main.js Normal file
View File

@ -0,0 +1,17 @@
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import '././assets/styles/border.css'
import '././assets/styles/reset.css'
import SvgIcon from './icons'
import '@/router/permission.js'
import axios from "axios";
axios.defaults.withCredentials=true
// 意思是携带cookie信息,保持session的一致性
createApp(App).use(store).use(router).use(ElementPlus).use(SvgIcon).mount('#app');

103
src/router/index.js Normal file
View File

@ -0,0 +1,103 @@
import { createRouter, createWebHashHistory } from 'vue-router'
import HomeView from '../views/login/index.vue'
const routes = [
// {
// path: '/home',
// name: '首页',
// // route level code-splitting
// // this generates a separate chunk (about.[hash].js) for this route
// // which is lazy-loaded when the route is visited.
// component: () => import('../views/home/index')
// },
{
path: '/',
name: '首页',
component: () => import('../views/layout/index'),
redirect: '/home',
children:[
{
path: '/home',
name: '首页',
component: () => import('../views/home/index')
},
{
path: '/welcome',
name: 'welcome',
component: () => import('../views/welcome/index')
},
{
path: '/busines',
name: '商家',
component: () => import('../views/bigType/index'),
},
{
path: '/product',
name: '商品管理',
component: () => import('../views/product/index')
},
{
path: '/user',
name: '用户',
component: () => import('../views/user/index')
},
{
path: '/employee',
name: '美甲师',
component: () => import('../views/employee/index')
},
{
path: '/modifyPassword',
name: '修改密码',
component: () => import('../views/modifyPassword/index')
},
{
path: '/order',
name: '订单',
component: () => import('../views/order/index')
},
{
path: '/personal',
name: '个人中心',
component: () => import('../views/personalCenter/index')
},
{
path: '/business',
name: '商家',
component: () => import('../views/business/index')
},
{
path: '/profile',
name: '详情',
component: () => import('../views/profile/index')
},
]
},
{
path: '/login',
name: 'login',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import( '../views/login/index.vue')
},
{
path: '/test',
name: 'test',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import( '../views/test/index.vue')
}
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
export default router

21
src/router/permission.js Normal file
View File

@ -0,0 +1,21 @@
import router from '@/router/index'
const whiteList=['/login']
router.beforeEach((to,from,next)=>{
let token=window.sessionStorage.getItem("token");
// console.log("token="+token);
// console.log("to.path="+to.path)
// 暂时放行
if(token){
if(to.path=='/login'){ // 如果是登录请求
next('/home') // 跳转后台管理页面
}else{
next(); // 放行
}
}else{
if(whiteList.includes(to.path)){
next() // 放行
}else{
next("/login") // 跳转登录页面
}
}
})

35
src/store/index.js Normal file
View File

@ -0,0 +1,35 @@
// import { createStore } from 'vuex'
//
// export default createStore({
// state: {
// },
// getters: {
// },
// mutations: {
// },
// actions: {
// },
// modules: {
// }
// })
import { createStore } from 'vuex'
import router from '@/router'
export default createStore({
state: {
},
getters: {
},
mutations: {
},
actions: {
// 安全退出
logout(){
window.sessionStorage.clear();
window.localStorage.clear();
router.replace('/login')
}
},
modules: {
}
})

108
src/util/axios.js Normal file
View File

@ -0,0 +1,108 @@
// 引入axios
import axios from 'axios';
import {request} from "axios";
//let baseUrl='http://localhost:8081/api/';
let baseUrl='http://154.8.193.216:1107/api/';
// 创建axios实例
const httpService = axios.create({
// url前缀-'http:xxx.xxx'
// baseURL: process.env.BASE_API, // 需自定义
baseURL: baseUrl,
withCredentials: true,
// 请求超时时间
timeout: 120000 // 需自定义
});
//添加请求和响应拦截器
// 添加请求拦截器
httpService.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
config.headers.token=window.sessionStorage.getItem('token');
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// 添加响应拦截器
httpService.interceptors.response.use(function (response) {
// 对响应数据做点什么
return response;
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});
/*网络请求部分*/
/*
* get请求
* url:请求地址
* params:参数
* */
export function get(url, params = {}) {
return new Promise((resolve, reject) => {
httpService({
url: url,
method: 'get',
params: params
}).then(response => {
resolve(response);
}).catch(error => {
reject(error);
});
});
}
/*
* post请求
* url:请求地址
* params:参数
* */
export function post(url, params = {}) {
return new Promise((resolve, reject) => {
httpService({
url: url,
method: 'post',
data: params
}).then(response => {
// console.log(response)
resolve(response);
}).catch(error => {
console.log(error)
reject(error);
});
});
}
/*
* 文件上传
* url:请求地址
* params:参数
* */
export function fileUpload(url, params = {}) {
return new Promise((resolve, reject) => {
httpService({
url: url,
method: 'post',
data: params,
headers: { 'Content-Type': 'multipart/form-data' }
}).then(response => {
resolve(response);
}).catch(error => {
reject(error);
});
});
}
export function getServerUrl(){
return baseUrl;
}
export default {
get,
post,
fileUpload,
getServerUrl
}

View File

@ -0,0 +1,116 @@
<template>
<el-dialog
model-value="dialogVisible"
:title="dialogTitle"
width="30%"
@close="handleClose"
>
<el-form
ref="formRef"
:model="form"
:rules="rules"
label-width="100px"
>
<el-form-item label="大类名称:" prop="name">
<el-input v-model="form.name" />
</el-form-item>
<el-form-item label="大类描述:" prop="remark">
<el-input v-model="form.remark" type="textarea" :rows="4"/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="handleConfirm">确认</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import {defineEmits, defineProps, ref, watch} from "vue";
import axios from "@/util/axios";
import { ElMessage } from "element-plus";
const props=defineProps({
id:{
type:Number,
default:-1,
required:true
},
dialogTitle:{
type:String,
default:'',
required:true
}
})
const form=ref({
id:-1,
name:"",
remark:""
})
const rules=ref({
name: [
{
required: true,
message: '请输入商品大类名称!'
}
],
remark: [
{
required: true,
message: '请输入商品大类描述!'
}
]
})
const formRef=ref(null);
const initFormData=async(id)=>{
const res=await axios.get("admin/bigType/"+id)
form.value=res.data.bigType;
console.log(form.value)
}
watch(
()=>props.id,
()=>{
//console.log("id="+props.id);
let id=props.id;
if(id!=-1){
initFormData(id)
}else{
formRef.value.resetFields();
form.value={
id:-1,
name:"",
remark:""
}
}
}
)
//
const emits=defineEmits(['update:modelValue','initBigTypeList'])
const handleClose=()=>{
console.log("xxx")
//
emits('update:modelValue',false)
}
const handleConfirm=()=>{
formRef.value.validate(async(valid)=>{
if(valid){
let result=await axios.post("admin/bigType/save",form.value)
let data=result.data;
if(data.code==0){
ElMessage.success("执行成功!");
formRef.value.resetFields();
emits("initBigTypeList");
handleClose();
}else{
ElMessage.error(data.msg);
}
}else{
console.log("fail")
return false
}
})
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,121 @@
<template>
<el-dialog
model-value="imageDialogVisible"
title="商品大类图片更换"
width="30%"
@close="handleClose"
center
>
<el-form
ref="formRef"
:model="form"
label-width="100px"
style="text-align: center"
>
<el-upload
:headers="headers"
class="avatar-uploader"
:action="getServerUrl()+'/admin/bigType/uploadImage'"
:show-file-list="false"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload"
>
<img v-if="imageUrl" :src="imageUrl" class="avatar" />
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
</el-upload>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button type="primary" @click="handleConfirm">确认更换</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import {defineEmits, defineProps, ref, watch} from "vue";
import axios,{getServerUrl} from "@/util/axios";
import { ElMessage } from "element-plus";
import { Plus } from '@element-plus/icons-vue'
const props=defineProps({
imageDialogValue:{
type:Object,
default:()=>{},
required:true
}
})
const form=ref({
id:-1,
image:''
})
const headers=ref({
token:window.sessionStorage.getItem('token')
})
const formRef=ref(null);
const imageUrl=ref("")
watch(
()=>props.imageDialogValue,
()=>{
form.value=props.imageDialogValue;
imageUrl.value=getServerUrl()+'/image/bigType/'+form.value.image
},
{deep:true,immediate:true}
)
const beforeAvatarUpload = (file) => {
const isJPG = file.type === 'image/jpeg'
const isLt2M = file.size / 1024 / 1024 < 2
if (!isJPG) {
ElMessage.error('图片必须是jpg格式')
}
if (!isLt2M) {
ElMessage.error('图片大小不能超过2M!')
}
return isJPG && isLt2M
}
const handleAvatarSuccess=(res)=>{
imageUrl.value=getServerUrl()+res.data.src;
form.value.image=res.data.title;
}
//
const emits=defineEmits(['update:modelValue','initBigTypeList'])
const handleClose=()=>{
console.log("xxx")
//
emits('update:modelValue',false)
}
const handleConfirm=async()=>{
let result=await axios.post("admin/bigType/save",form.value)
let data=result.data;
if(data.code==0){
ElMessage.success("执行成功!");
formRef.value.resetFields();
emits("initBigTypeList");
handleClose();
}else{
ElMessage.error(data.msg);
}
}
</script>
<style >
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #409eff;
}
.el-icon.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
text-align: center;
}
.avatar {
width: 178px;
height: 178px;
display: block;
}
</style>

144
src/views/bigType/index.vue Normal file
View File

@ -0,0 +1,144 @@
<template>
<el-card>
<el-row :gutter="20" class="header">
<el-col :span="7">
<el-input placeholder="请输入商品大类名称..." clearable v-model="queryForm.query"></el-input>
</el-col>
<el-button type="primary" :icon="Search" @click="initBigTypeList">搜索</el-button>
<el-button type="primary" :icon="DocumentAdd" @click="handleDialogValue()" >添加 商品大类</el-button>
</el-row>
<el-table :data="tableData" stripe style="width: 100%">
<el-table-column prop="id" label="#ID" width="80" />
<el-table-column prop="name" label="商品大类名称" width="200" />
<el-table-column prop="image" label="商品大类图片" width="200">
<template v-slot="scope">
<img :src="getServerUrl()+'/image/bigType/'+scope.row.image" width="80" height="80"/>
</template>
</el-table-column>
<el-table-column prop="remark" label="商品大类描述"/>
<el-table-column prop="action" label="操作" width="300" >
<template v-slot="scope">
<el-button type="success" @click="handleChangeImage(scope.row)">更换图片</el-button>
<el-button type="primary" :icon="Edit"
@click="handleDialogValue(scope.row.id)"></el-button>
<el-button type="danger" :icon="Delete"
@click="handleDelete(scope.row.id)"></el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
v-model:currentPage="queryForm.pageNum"
v-model:page-size="queryForm.pageSize"
:page-sizes="[5, 10, 15, 20,25]"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</el-card>
<Dialog v-model="dialogVisible" :id="id" :dialogTitle="dialogTitle"
@initBigTypeList="initBigTypeList"></Dialog>
<ImageDialog v-model="imageDialogVisible" :imageDialogValue="imageDialogValue"
@initBigTypeList="initBigTypeList"/>
</template>
<script setup>
import {Search ,Delete,Edit,DocumentAdd} from '@element-plus/icons-vue'
import { ref } from 'vue'
import axios, {getServerUrl} from "@/util/axios";
import Dialog from "@/views/bigType/dialog/index.vue";
import { ElMessage, ElMessageBox } from 'element-plus'
import ImageDialog from '@/views/bigType/imageDialog/index.vue'
const handleChangeImage=(row)=>{
imageDialogVisible.value=true
imageDialogValue.value=JSON.parse(JSON.stringify(row))
}
const imageDialogVisible=ref(false)
const imageDialogValue=ref({})
const id=ref(-1)
const dialogTitle=ref('');
const dialogVisible=ref(false)
const handleDialogValue=(bigTypeId)=>{
// console.log("bigTypeId="+bigTypeId)
if(bigTypeId){
id.value=bigTypeId;
dialogTitle.value="商品大类修改"
// console.log(dialogTitle.value)
}else{
id.value=-1;
dialogTitle.value="商品大类添加"
// console.log(dialogTitle.value)
}
dialogVisible.value=true
}
const queryForm=ref({
query:'',
pageNum:1,
pageSize:10
})
const total=ref(0)
const tableData =ref([])
const initBigTypeList=async()=>{
const res=await axios.post("admin/bigType/list",queryForm.value);
tableData.value=res.data.bigTypeList;
total.value=res.data.total;
}
initBigTypeList();
const handleSizeChange = (pageSize) => {
queryForm.value.pageNum=1;
queryForm.value.pageSize=pageSize;
initBigTypeList();
}
const handleCurrentChange = (pageNum) => {
queryForm.value.pageNum=pageNum;
initBigTypeList();
}
const handleDelete=(id)=>{
ElMessageBox.confirm(
'您确定要删除这条记录吗?',
'系统提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(async() => {
let res=await axios.get('admin/bigType/delete/'+id)
if(res.data.code==0){
ElMessage({
type: 'success',
message: '删除成功',
})
initBigTypeList();
}else{
ElMessage({
type: 'error',
message: res.data.msg,
})
}
})
.catch(() => {
})
}
</script>
<style lang="scss" scoped>
.header{
padding-bottom: 16px;
box-sizing: border-box;
}
.el-pagination{
padding-top: 15px;
box-sizing: border-box;
}
</style>

View File

@ -0,0 +1,166 @@
<template>
<el-dialog
model-value="BdialogaddVisible"
:title="dialogTitle"
width="30%"
@close="handleClose"
>
<el-form
ref="formRef"
:model="form"
label-width="100px"
>
<el-form-item label="门店头像" prop="businessAvatar">
<el-input v-model="form.businessAvatar" />
</el-form-item>
<el-form-item label="用户账号" prop="userAccount">
<el-input v-model="form.userAccount" />
</el-form-item>
<el-form-item label="用户密码" prop="userPassword">
<el-input v-model="form.userPassword" />
</el-form-item>
<el-form-item label="门店名" prop="businessName">
<el-input v-model="form.businessName" />
</el-form-item>
<el-form-item label="门店手机号" prop="businessPhone">
<el-input v-model="form.businessPhone" />
</el-form-item>
<el-form-item label="店铺详细地址" prop="address">
<el-input v-model="form.address" />
</el-form-item>
<el-form-item label="门店简介" prop="businessProfile">
<el-input v-model="form.businessProfile" />
</el-form-item>
<!-- <el-form-item label="商家相册" prop="businessImages">-->
<!-- <el-input v-model="form.businessImages" />-->
<!-- </el-form-item>-->
<!-- <el-form-item label="分类ID" prop="categoryId">-->
<!-- <el-input v-model="form.categoryId" />-->
<!-- </el-form-item>-->
<el-form-item label="店主名" prop="shopkeeper">
<el-input v-model="form.shopkeeper" />
</el-form-item>
<el-form-item label="营业执照" prop="license">
<el-input v-model="form.license" />
</el-form-item>
<!-- <el-form-item label="身份证正反面" prop="frontIdCard">-->
<!-- <el-input v-model="form.frontIdCard" />-->
<!-- -->
<!-- </el-form-item>-->
<!-- <el-form-item label="身份证反面" prop="backIdCard">-->
<!-- <el-input v-model="form.backIdCard" /> -->
<!-- </el-form-item>-->
<el-form-item label="银行卡号" prop="bankCard">
<el-input v-model="form.bankCard" />
</el-form-item>
<el-form-item label="开始营业时间" prop="startBusiness">
<el-time-select
v-model="value1"
style="width: 100%"
start="00:00"
step="00:15"
end="23:45"
placeholder="选择开始营业时间"
/>
</el-form-item>
<el-form-item label="结束营业时间" prop="endBusiness">
<el-time-select
v-model="value2"
style="width: 100%"
start="00:00"
step="00:15"
end="23:45"
placeholder="选择结束营业时间"
/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="handleConfirm">确认</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import {defineEmits, defineProps, ref, watch} from "vue";
import axios from "@/util/axios";
import { ElMessage } from "element-plus";
const value1 = ref('')
const value2 = ref('')
const props=defineProps({
dialogTitle:{
type:String,
default:'',
required:true
}
})
const form=ref({
userAccount:"",
userPassword:"",
address: "",
backIdCard: "",
bankCard: "",
businessAvatar: "",
businessImages: "",
businessName: "",
businessPhone: "",
businessProfile: "",
categoryId: null,
endBusiness: "",
frontIdCard: "",
license: "",
shopkeeper: "",
startBusiness: ""
})
// const rules=ref({
// name: [
// {
// required: true,
// message: ''
// }
// ],
// remark: [
// {
// required: true,
// message: ''
// }
// ]
// })
const formRef=ref(null);
//
const emits=defineEmits(['update:modelValue','initBusinessList'])
const handleClose=()=>{
console.log("用户添加关闭xxx")
//
emits('update:modelValue',false)
}
const handleConfirm=()=>{
formRef.value.validate(async(valid)=>{
if(valid){
form.value.startBusiness=value1.value;
form.value.endBusiness=value2.value;
let result=await axios.post("business/add",form.value)
console.log(result.data)
let data=result.data;
if(data.code==0){
ElMessage.success("执行添加成功!");
formRef.value.resetFields();
emits("initBusinessList");
handleClose();
}else{
ElMessage.error(data.description);
}
}else{
console.log("fail")
return false
}
})
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,159 @@
<template>
<el-dialog
model-value="BdialogVisible"
:title="dialogTitle"
width="30%"
@close="handleClose"
>
<el-form
ref="formRef"
:model="form"
label-width="100px"
>
<el-form-item label="门店头像" prop="businessAvatar">
<img :src="form.businessAvatar" width="50" height="50"/>
</el-form-item>
<el-form-item label="门店名" prop="businessName">
{{form.businessName}}
</el-form-item>
<el-form-item label="门店手机号" prop="businessPhone">
{{form.businessPhone}}
</el-form-item>
<el-form-item label="店铺详细地址" prop="address">
{{form.address}}
</el-form-item>
<el-form-item label="门店简介" prop="businessProfile">
{{form.businessProfile}}
</el-form-item>
<el-form-item label="商家相册" prop="businessImages">
{{form.businessImages}}
</el-form-item>
<el-form-item label="分类ID" prop="categoryId">
{{form.categoryId}}
</el-form-item>
<el-form-item label="店主名" prop="shopkeeper">
{{form.shopkeeper}}
</el-form-item>
<el-form-item label="营业执照" prop="license">
{{form.license}}
</el-form-item>
<el-form-item label="身份证正面" prop="frontIdCard">
{{form.frontIdCard}}
</el-form-item>
<el-form-item label="身份证反面" prop="backIdCard">
{{form.backIdCard}}
</el-form-item>
<el-form-item label="银行卡号" prop="bankCard">
{{form.bankCard}}
</el-form-item>
<el-form-item label="开始营业时间" prop="startBusiness">
{{form.startBusiness}}
</el-form-item>
<el-form-item label="结束营业时间" prop="endBusiness">
{{form.endBusiness}}
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
{{form.createTime}}
</el-form-item>
</el-form>
<template #footer v-if="title">
<span class="dialog-footer">
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="handleConfirm">审核成功</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import {defineEmits, defineProps, ref, watch} from "vue";
import axios from "@/util/axios";
import { ElMessage } from "element-plus";
const props=defineProps({
bussinessId:{
type: String,
default:-1,
required:true
},
dialogTitle:{
type:String,
default:'',
required:true
}
})
const form=ref({
id:null,
address: "",
backIdCard: "",
bankCard: "",
businessAvatar: "",
businessImages: "",
businessName: "",
businessPhone: "",
businessProfile: "",
categoryId: null,
endBusiness: "",
frontIdCard: "",
license: "",
state:null,
shopkeeper: "",
startBusiness: "",
createTime:""
})
const formRef=ref(null);
const title=ref(false);
const initFormData=async(id)=>{
// console.log(id)
// const res=await axios.get("business/getById",{id: id})
// // form.value=res.data.data;
// form.value=res.data.data;
const res=await axios.post("business/list/page",{id: id})
console.log(res.data.data)
form.value=res.data.data.records[0];
}
watch(
()=>props.bussinessId,
()=>{
console.log("id="+props.dialogTitle);
console.log(props.bussinessId)
if (props.dialogTitle=='审核商家'){
title.value=true;
}
let id=props.bussinessId;
initFormData(id)
}
)
//
const emits=defineEmits(['update:modelValue','initBusinessList'])
const handleClose=()=>{
console.log("xxx")
//
emits('update:modelValue',false)
}
const handleConfirm = () => {
form.value.state=1
formRef.value.validate(async (valid) => {
if (valid) {
let result = await axios.post("business/update", form.value)
console.log(result.data.data)
let data = result.data;
if (data.code == 0) {
ElMessage.success("执行成功!");
formRef.value.resetFields();
emits("initBusinessList");
handleClose();
} else {
ElMessage.error(data.description);
}
} else {
console.log("fail")
return false
}
})
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,258 @@
<template>
<el-card>
<el-row :gutter="20" class="header">
<el-col :span="7">
<el-input placeholder="请输入用户昵称..." clearable v-model="formPage.businessName"></el-input>
</el-col>
<el-button type="button" :icon="Search" @click="initBusinessList">搜索</el-button>
<el-button type="primary" :icon="DocumentAdd" @click="handleAddDialogValue()" >添加商家</el-button>
<el-radio-group v-model="isCollapse" style="margin-bottom: 20px;margin-left: 20px">
<el-radio-button :value="true" @click="handleCollapse(true)">审核</el-radio-button>
<el-radio-button :value="false" @click="handleCollapse(false)">未审核</el-radio-button>
</el-radio-group>
</el-row>
<el-table :data="tableData" stripe style="width: 100%;" showOverflowTooltip>
<el-table-column prop="id" label="#ID" width="80" fixed="fixed" />
<el-table-column prop="businessName" label="商家昵称" width="200" />
<el-table-column prop="businessAvatar" label="头像" width="150" >
<template v-slot="scope">
<el-popover
placement="right-start"
:width="200"
trigger="hover"
:content="scope.row.businessAvatar"
>
<template #reference>
<img :src="scope.row.businessAvatar" width="50" height="50"/>
</template>
</el-popover>
</template>
</el-table-column>
<el-table-column prop="businessPhone" label="电话号码" width="120" />
<el-table-column prop="state" label="状态" width="80">
<template v-slot="scope">
<div v-if="scope.row.state===0" style="color: red">
禁用
</div>
<div style="color: lawngreen" v-else>
启用
</div>
</template>
</el-table-column>
<el-table-column prop="storeStatus" label="店铺状态" width="80">
<template v-slot="scope">
<div v-if="scope.row.storeStatus===0" style="color: red">
休业
</div>
<div style="color: lawngreen" v-else>
营业
</div>
</template>
</el-table-column>
<el-table-column prop="createTime" label="注册日期" width="200"/>
<el-table-column prop="updateTime" label="最后登录日期" width="200"/>
<el-table-column prop="action" fixed="right" label="操作" min-width="270" v-if="isCollapse||isCollapse===undefined">
<template v-slot="scope" >
<el-button type="success" size="small" @click="handleRouter(scope.row)" >
商品管理
</el-button>
<el-button type="success" size="small" @click="handleDialogValue(scope.row.id)">
详情
</el-button>
<el-button type="primary" size="small" :icon="Edit" @click="handleUpdateDialogValue(scope.row.id)"></el-button>
</template>
</el-table-column>
<el-table-column prop="action" fixed="right" label="操作" min-width="120" v-else>
<template v-slot="scope" >
<el-button type="primary" size="small" @click="handleClick(scope.row.id)">审核</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
v-model:currentPage="formPage.current"
v-model:page-size="formPage.pageSize"
:page-sizes="[10, 20, 30, 40,50]"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</el-card>
<Dialog v-model="BdialogVisible" :bussinessId="bussinessId" :dialogTitle="dialogTitle"
@initBusinessList="initBusinessList"></Dialog>
<AddDialog v-model="BdialogaddVisible" :dialogTitle="dialogTitle"
@initBusinessList="initBusinessList"></AddDialog>
<UpdateDialog v-model="BdialogUpdateVisible" :bussinessId="bussinessId" :dialogTitle="dialogTitle"
@initBusinessList="initBusinessList"></UpdateDialog>
</template>
<script setup>
import {Search,DocumentAdd,Edit} from '@element-plus/icons-vue'
import {ref, watch} from 'vue'
import axios from "@/util/axios";
import { ElNotification } from 'element-plus'
import Dialog from '@/views/business/dialog/index.vue'
import AddDialog from '@/views/business/addDialog/index.vue'
import UpdateDialog from '@/views/business/updateDialog/index.vue'
import axiosUtil from "@/util/axios";
import {ElMessage} from "element-plus";
import {useRouter} from "vue-router"
import {request} from "axios";
const isCollapse=ref();
const RCollapse=ref([]);
const LCollapse=ref([]);
const router=useRouter();
const bussinessId=ref('')
const dialogTitle=ref('');
const BdialogaddVisible=ref(false)
const BdialogVisible=ref(false)
const BdialogUpdateVisible=ref(false)
const handleClick=(id)=>{
bussinessId.value=id;
// console.log(bussinessId.value)
dialogTitle.value="审核商家"
BdialogVisible.value=true
}
const handleRouter=(temp)=>{
console.log(temp.id)
router.push({path:'/product',query:{product: JSON.stringify(temp)}})
}
const handleDialogValue=(id)=>{
//console.log("bigTypeId="+bussinessId)
// if(bussinessId){
bussinessId.value=id;
dialogTitle.value="商家详情"
BdialogVisible.value=true
}
const handleUpdateDialogValue=(id)=>{
// console.log(bussinessId)
bussinessId.value=id;
// console.log(bussinessId.value)
dialogTitle.value="商家修改"
BdialogUpdateVisible.value=true
}
const handleAddDialogValue=()=>{
dialogTitle.value="商家添加"
BdialogaddVisible.value=true
}
const formPage=ref({
businessName:'',
current:1,
pageSize:10
})
// watch(formPage.value.businessName,()=>{
// // console.log(id.value)
// initBusinessList();
// })
const total=ref(0)
const tableData =ref([])
const handleCollapse=async (temp)=>{
console.log(temp)
const res=await axios.post("business/list");
RCollapse.value.splice(0,RCollapse.value.length);
LCollapse.value.splice(0,LCollapse.value.length);
// console.log(res.data.data.records)
tableData.value=res.data.data;
tableData.value.forEach(item => {
if(item.state){
RCollapse.value.push(item)
}
else{
LCollapse.value.push(item)
}
//console.log(item.state);
});
console.log("RCollapse"+RCollapse.value);
console.log("LCollapse"+LCollapse.value);
if (temp){
tableData.value=RCollapse.value
total.value=Number(RCollapse.value.length);
}else {
tableData.value=LCollapse.value
total.value=Number(LCollapse.value.length);
}
}
const initBusinessList=async()=>{
// console.log(formPage.value)
const res=await axios.post("business/list/page",formPage.value);
// console.log(res.data.data.records)
isCollapse.value=undefined;
tableData.value=res.data.data.records;
total.value=Number(res.data.data.total);
}
// const initBusinessList=async()=>{
//
// if(!id.value)
// {
// const res=await axios.post("business/list/page",formPage.value);
// // console.log(res.data.data.records)
// tableData.value=res.data.data.records;
// // console.log(res.data.data.total)
// total.value=res.data.data.records.length;
// }
// else{
// const res=await axios.post("business/list/page", {businessName:id.value});
// // console.log(res.data.data)
// //const temp=[];
// // temp.push(res.data.data)
// tableData.value=res.data.data.records;
// total.value=res.data.data.records.length;
// }
//
// }
initBusinessList();
const handleSizeChange = (pageSize) => {
formPage.value.current=1;
formPage.value.pageSize=pageSize;
initBusinessList();
}
const handleCurrentChange = (pageNum) => {
formPage.value.current=pageNum;
initBusinessList();
}
</script>
<style lang="scss" scoped>
.header{
padding-bottom: 16px;
box-sizing: border-box;
}
.el-pagination{
padding-top: 15px;
box-sizing: border-box;
}
</style>

View File

@ -0,0 +1,138 @@
<template>
<el-dialog
model-value="BdialogUpdateVisible"
:title="dialogTitle"
width="30%"
@close="handleClose"
>
<el-form
ref="formRef"
:model="form"
label-width="100px"
>
<el-form-item label="门店头像" prop="businessAvatar">
<el-input v-model="form.businessAvatar" />
</el-form-item>
<el-form-item label="门店名" prop="businessName">
<el-input v-model="form.businessName" />
</el-form-item>
<el-form-item label="门店手机号" prop="businessPhone">
<el-input v-model="form.businessPhone" />
</el-form-item>
<el-form-item label="店铺详细地址" prop="address">
<el-input v-model="form.address" />
</el-form-item>
<el-form-item label="门店简介" prop="businessProfile">
<el-input v-model="form.businessProfile" />
</el-form-item>
<el-form-item label="商家相册" prop="businessImages">
<el-input v-model="form.businessImages" />
</el-form-item>
<el-form-item label="分类ID" prop="categoryId">
<el-input v-model="form.categoryId" />
</el-form-item>
<el-form-item label="店铺关联用户ID" prop="userId">
<el-input v-model="form.userId" />
</el-form-item>
<el-form-item label="状态" prop="state">
<el-input v-model="form.state" />
</el-form-item>
<el-form-item label="店铺状态" prop="storeStatus">
<el-input v-model="form.state" />
</el-form-item>
<el-form-item label="开始营业时间" prop="startBusiness">
<el-input v-model="form.startBusiness" />
</el-form-item>
<el-form-item label="结束营业时间" prop="endBusiness">
<el-input v-model="form.endBusiness" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="handleConfirm">确认</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import {defineEmits, defineProps, ref, watch} from "vue";
import axios from "@/util/axios";
import {ElMessage} from "element-plus";
const props = defineProps({
bussinessId: {
type: String,
default: '',
required: true
},
dialogTitle: {
type: String,
default: '',
required: true
}
})
const form = ref({
id:"",
address: "",
userId: null,
businessAvatar: "",
businessImages: "",
businessName: "",
businessPhone: "",
businessProfile: "",
categoryId: null,
endBusiness: "",
state:null,
storeStatus: null,
startBusiness: ""
})
const formRef = ref(null);
const initFormData = async (id) => {
//console.log(id)
const res=await axios.post("business/list/page",{id: id})
form.value=res.data.data.records[0];
//console.log(res.data.data)
// console.log("bussinessId"+props.bussinessId)
}
watch(
() => props.bussinessId,
() => {
// console.log("id=" + props.businessId);
let id = props.bussinessId;
initFormData(id)
}
)
//
const emits = defineEmits(['update:modelValue', 'initBusinessList'])
const handleClose = () => {
console.log("xxx")
//
emits('update:modelValue', false)
}
const handleConfirm = () => {
formRef.value.validate(async (valid) => {
if (valid) {
let result = await axios.post("business/update", form.value)
console.log(result.data.data)
let data = result.data;
if (data.code == 0) {
ElMessage.success("执行成功!");
formRef.value.resetFields();
emits("initBusinessList");
handleClose();
} else {
ElMessage.error(data.description);
}
} else {
console.log("fail")
return false
}
})
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,118 @@
<template>
<el-dialog
model-value="edialogaddVisible"
:title="dialogTitle"
width="30%"
@close="handleClose"
>
<el-form
ref="formRef"
:model="form"
:rules="rules"
label-width="100px"
>
<el-form-item label="用户账号" prop="userAccount">
<el-input v-model="form.userAccount" />
</el-form-item>
<el-form-item label="用户密码" prop="userPassword">
<el-input v-model="form.userPassword" />
</el-form-item>
<el-form-item label="用户昵称" prop="manicuristName">
<el-input v-model="form.manicuristName"/>
</el-form-item>
<el-form-item label="性别" prop="gender">
<el-input v-model="form.gender"/>
</el-form-item>
<el-form-item label="电话" prop="phone">
<el-input v-model="form.phone"/>
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="form.email"/>
</el-form-item>
<el-form-item label="工作名称" prop="specialties">
<el-input v-model="form.specialties"/>
</el-form-item>
<el-form-item label="评分" prop="rating">
<el-input v-model="form.rating"/>
</el-form-item>
<el-form-item label="余额" prop="salary">
<el-input v-model="form.salary"/>
</el-form-item>
<el-form-item label="认证号" prop="certification_number">
<el-input v-model="form.certification_number"/>
</el-form-item>
<el-form-item label="认证路径" prop="certificate_path">
<el-input v-model="form.certificate_path"/>
</el-form-item>
<el-form-item label="组织认证" prop="issuing_authority">
<el-input v-model="form.issuing_authority"/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="handleConfirm">确认</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import {defineEmits, defineProps, ref, watch} from "vue";
import axios from "@/util/axios";
import { ElMessage } from "element-plus";
const props=defineProps({
dialogTitle:{
type:String,
default:'',
required:true
}
})
const form=ref({
userAccount:"",
userPassword:"",
certificate_path: "",
certification_number: "",
email: "",
gender: null,
issuing_authority: "",
manicuristName: "",
phone: "",
rating: null,
salary: null,
specialties: ""
})
const formRef=ref(null);
//
const emits=defineEmits(['update:modelValue','initEmployeeList'])
const handleClose=()=>{
console.log("用户添加关闭xxx")
//
emits('update:modelValue',false)
}
const handleConfirm=()=>{
formRef.value.validate(async(valid)=>{
if(valid){
let result=await axios.post("manicurist/adminAdd",form.value)
console.log(result.data)
let data=result.data;
if(data.code==0){
ElMessage.success("执行添加成功!");
formRef.value.resetFields();
emits("initEmployeeList");
handleClose();
}else{
ElMessage.error(data.description);
}
}else{
console.log("fail")
return false
}
})
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,109 @@
<template>
<el-dialog
model-value="edialogVisible"
:title="dialogTitle"
width="30%"
@close="handleClose"
>
<el-form
ref="formRef"
:model="form"
:rules="rules"
label-width="100px"
>
<el-form-item label="头像" prop="manicuristAvatar">
<img :src="form.manicuristAvatar" width="80px" height="80px"/>
</el-form-item>
<el-form-item label="用户昵称" prop="manicuristName">
{{form.manicuristName}}
</el-form-item>
<el-form-item label="性别" prop="gender">
<template v-if="form.gender==0"></template>
<template v-else></template>
</el-form-item>
<el-form-item label="电话" prop="phone">
{{form.phone}}
</el-form-item>
<el-form-item label="邮箱" prop="email">
{{form.email}}
</el-form-item>
<el-form-item label="工作名称" prop="specialties">
{{form.specialties}}
</el-form-item>
<el-form-item label="工作时长" prop="employment_date">
{{form.employment_date}}
</el-form-item>
<el-form-item label="评分" prop="rating">
{{form.rating}}
</el-form-item>
<el-form-item label="余额" prop="salary">
{{form.salary}}
</el-form-item>
</el-form>
</el-dialog>
</template>
<script setup>
import {defineEmits, defineProps, ref, watch} from "vue";
import axios from "@/util/axios";
import { ElMessage } from "element-plus";
const props=defineProps({
id:{
type:String,
default:'',
required:true
},
dialogTitle:{
type:String,
default:'',
required:true
}
})
const form=ref({
id:null,
email: "",
gender: null,
phone: "",
userId:"",
businessId: "",
manicuristName: "",
employment_date: "",
specialties: "",
rating: null,
salary: null,
isDelete: null,
createTime: "",
updateTime: "",
manicuristAvatar: "",
manStatus: null
})
const formRef=ref(null);
const initFormData=async(id)=>{
const res = await axios.get("manicurist/queryById", {manicuristId: id})
form.value=res.data.data;
//console.log(res.data.data)
}
watch(
()=>props.id,
()=>{
//console.log("id="+props.id);
let id=props.id;
initFormData(id)
}
)
//
const emits=defineEmits(['update:modelValue','initEmployeeList'])
const handleClose=()=>{
console.log("xxx")
//
emits('update:modelValue',false)
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,382 @@
<template>
<!-- <g-gantt-chart-->
<!-- chart-start="00:00"-->
<!-- chart-end="23:59"-->
<!-- precision="hour"-->
<!-- date-format="HH:mm"-->
<!-- bar-start="beginDate"-->
<!-- bar-end="endDate"-->
<!-- grid-->
<!-- >-->
<!-- <template #upper-timeunit>-->
<!-- <h1>-->
<!-- {{-->
<!-- `${weekRangeInChina.currentWeekStart} / ${weekRangeInChina.currentWeekEnd}`-->
<!-- }}-->
<!-- </h1>-->
<!-- </template>-->
<!-- <g-gantt-row-->
<!-- v-for="(item, index) in context"-->
<!-- :key="index"-->
<!-- :bars="item"-->
<!-- :label="item[0].week"-->
<!-- highlight-on-hover-->
<!-- />-->
<!-- </g-gantt-chart>-->
<!--=================================================================-->
<el-card>
<el-row :gutter="20" class="header">
<el-col :span="7">
<el-input placeholder="请输入用户昵称..." clearable v-model="query" ></el-input>
</el-col>
<el-button type="button" :icon="Search" @click="initEmployeeList">搜索</el-button>
<el-button type="primary" :icon="DocumentAdd" @click="handleAddDialogValue()" >添加用户</el-button>
</el-row>
<el-table :data="tableData" stripe style="width: 100%;" showOverflowTooltip>
<el-table-column prop="id" label="#ID" width="80" />
<el-table-column prop="manicuristName" label="用户昵称" width="200" />
<el-table-column prop="manicuristAvatar" label="头像" width="200">
<template v-slot="scope">
<img :src="scope.row.manicuristAvatar" width="50" height="50"/>
</template>
</el-table-column>
<el-table-column prop="rating" label="评分" width="200px">
<template v-slot="scope" style="height: 100px">
<div class="demo-rate-block">
<el-rate v-model="scope.row.rating" :colors="colors"/>
{{scope.row.rating}}
</div>
</template>
</el-table-column>
<el-table-column prop="salary" label="余额" />
<el-table-column prop="createTime" label="注册日期" width="200"/>
<el-table-column prop="updateTime" label="最后登录日期" width="200"/>
<el-table-column prop="action" fixed="right" label="操作" min-width="220">
<template v-slot="scope" >
<!-- <el-button @click="handleRouter(scope.row.id)" type="primary" :icon="DocumentAdd" >审核美甲师</el-button>-->
<el-button type="success" size="small" @click="handleDialogValue(scope.row.id)">
详情
</el-button>
<el-button type="primary" size="small" :icon="Edit" @click="handleUpdateDialogValue(scope.row.id)"></el-button>
<el-button type="danger" size="small" :icon="Delete" @click="handleDelete(scope.row.id)"></el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
v-model:currentPage="queryForm.current"
v-model:page-size="queryForm.pageSize"
:page-sizes="[10, 20, 30, 40,50]"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</el-card>
<Dialog v-model="edialogVisible" :id="id" :dialogTitle="dialogTitle"
@initEmployeeList="initEmployeeList"></Dialog>
<AddDialog v-model="edialogaddVisible" :dialogTitle="dialogTitle"
@initEmployeeList="initEmployeeList"></AddDialog>
<UpdateDialog v-model="edialogUpdateVisible" :id="id" :dialogTitle="dialogTitle"
@initEmployeeList="initEmployeeList"></UpdateDialog>
</template>
<script setup>
import {Search,DocumentAdd,Delete,Edit} from '@element-plus/icons-vue'
import {ref, watch} from 'vue'
import axios from "@/util/axios";
import Dialog from '@/views/employee/dialog/index.vue'
import AddDialog from '@/views/employee/addDialog/index.vue'
import UpdateDialog from '@/views/employee/updateDialog/index.vue'
import { ElMessage,ElMessageBox } from "element-plus";
import { GGanttChart, GGanttRow } from "@infectoone/vue-ganttastic";
const queryForm=ref({
current:1,
pageSize:10
})
const query=ref();
const total=ref(0)
const tableData =ref([])
const id=ref('')
const dialogTitle=ref('');
const edialogaddVisible=ref(false)
const edialogVisible=ref(false)
const edialogUpdateVisible=ref(false)
const handleRouter=(id)=>{
bussinessId.value=id;
router.push({path:'/profile',query:{id: bussinessId.value}})
}
const handleDialogValue=(ids)=>{
id.value=ids;
dialogTitle.value="美甲师详情"
edialogVisible.value=true
}
const handleUpdateDialogValue=(ids)=>{
// console.log(id)
id.value=ids;
// console.log(id.value)
dialogTitle.value="美甲师修改"
edialogUpdateVisible.value=true
}
const handleAddDialogValue=()=>{
dialogTitle.value="美甲师添加"
edialogaddVisible.value=true
}
const handleDelete=(ids)=>{
// console.log(ids)
ElMessageBox.confirm(
'您确定要删除这条记录吗?',
'系统提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(async() => {
let res=await axios.post('manicurist/deleteMan?id='+Number(ids))
if(res.data.code==0){
ElMessage({
type: 'success',
message: '删除成功',
})
initSmallTypeList();
}else{
ElMessage({
type: 'error',
message: res.data.description,
})
}
})
.catch(() => {
})
// initUserList();
}
watch(query,()=>{
// console.log(query.value)
initEmployeeList();
},10000)
const initEmployeeList=async()=>{
if (!query.value){
const res=await axios.post("manicurist/queryAll");
// console.log(res.data)
tableData.value=res.data.data;
total.value=res.data.data.length;
}else{
const res = await axios.get("manicurist/queryById", {manicuristId: query.value});
// console.log(res.data)
const temp = []
temp.push(res.data.data)
tableData.value = temp;
total.value = 1;
}
}
initEmployeeList();
const handleSizeChange = (pageSize) => {
queryForm.value.current=1;
queryForm.value.pageSize=pageSize;
initEmployeeList();
}
const handleCurrentChange = (current) => {
queryForm.value.current=current;
initEmployeeList();
}
// =============================================
const colors = ref(['#99A9BF', '#F7BA2A', '#FF9900'])
// =========================================================================================================
const context = ref([
[
{
week: "星期一",
beginDate: "06:00",
endDate: "22:00",
ganttBarConfig: {
id: "0",
hasHandles: true,
label: "需求收集和分析 负责人:小张",
style: {
background: "#e96560"
}
}
}
],
[
{
week: "星期二",
beginDate: "09:00",
endDate: "18:00",
ganttBarConfig: {
id: "1",
hasHandles: true,
label: "系统设计 负责人:小强",
style: {
background: "#5ccfa3"
}
}
}
],
[
{
week: "星期三",
beginDate: "07:00",
endDate: "20:00",
ganttBarConfig: {
id: "2",
hasHandles: true,
label: "编码实现 负责人:老李",
style: {
background: "#77d6fa"
}
}
}
],
[
{
week: "星期四",
beginDate: "06:00",
endDate: "21:00",
ganttBarConfig: {
id: "3",
hasHandles: true,
label: "编码实现 负责人:小明",
style: {
color: "#fff",
background: "#1b2a47"
}
}
}
],
[
{
week: "星期五",
beginDate: "05:00",
endDate: "19:00",
ganttBarConfig: {
id: "4",
hasHandles: true,
label: "内部测试 负责人:小雪",
style: {
background: "#5ccfa3"
}
}
}
],
[
{
week: "星期六",
beginDate: "10:00",
endDate: "22:00",
ganttBarConfig: {
id: "5",
hasHandles: true,
label: "系统优化和文档整理 负责人:小欣",
style: {
background: "#f8bc45"
}
}
}
],
[
{
week: "星期天",
beginDate: "04:00",
endDate: "23:59",
ganttBarConfig: {
id: "6",
immobile: false,
hasHandles: false,
label: "部署和上线 负责人:老王",
style: {
background: "#f3953d"
}
}
}
]
]);
const getWeekRange=()=> {
const today = new Date();
const dayOfWeek = today.getDay();
const startDate = new Date(today);
startDate.setDate(today.getDate() - dayOfWeek + 1);
const endDate = new Date(startDate);
endDate.setDate(startDate.getDate() + 6);
const formatDate = date => {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
};
const currentWeekStart = formatDate(startDate);
const currentWeekEnd = formatDate(endDate);
return {
currentWeekStart,
currentWeekEnd
};
}
const weekRangeInChina = getWeekRange();
</script>
<style lang="scss" scoped>
.header{
padding-bottom: 16px;
box-sizing: border-box;
}
.el-pagination{
padding-top: 15px;
box-sizing: border-box;
}
.demo-rate-block {
padding: 15px 0;
text-align: center;
border-right: solid 1px var(--el-border-color);
display: inline-block;
width: 49%;
box-sizing: border-box;
}
.demo-rate-block:last-child {
border-right: none;
}
.demo-rate-block .demonstration {
display: block;
color: var(--el-text-color-secondary);
font-size: 14px;
margin-bottom: 20px;
}
</style>

View File

@ -0,0 +1,127 @@
<template>
<el-dialog
model-value="edialogUpdateVisible"
:title="dialogTitle"
width="30%"
@close="handleClose"
>
<el-form
ref="formRef"
:model="form"
label-width="100px"
>
<el-form-item label="头像" prop="manicuristAvatar">
<el-input v-model="form.manicuristAvatar"/>
</el-form-item>
<el-form-item label="用户昵称" prop="manicuristName">
<el-input v-model="form.manicuristName"/>
</el-form-item>
<el-form-item label="性别" prop="gender">
<el-input v-model="form.gender"/>
</el-form-item>
<el-form-item label="电话" prop="phone">
<el-input v-model="form.phone"/>
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="form.email"/>
</el-form-item>
<el-form-item label="工作名称" prop="specialties">
<el-input v-model="form.specialties"/>
</el-form-item>
<el-form-item label="关联商家ID" prop="businessId">
<el-input v-model="form.businessId"/>
</el-form-item>
<el-form-item label="工作时长" prop="employment_date">
<el-input v-model="form.employment_date"/>
</el-form-item>
<el-form-item label="评分" prop="rating">
<el-input v-model="form.rating"/>
</el-form-item>
<el-form-item label="余额" prop="salary">
<el-input v-model="form.salary"/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="handleConfirm">确认</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import {defineEmits, defineProps, ref, watch} from "vue";
import axios from "@/util/axios";
import {ElMessage} from "element-plus";
const props = defineProps({
id: {
type: String,
default: '',
required: true
},
dialogTitle: {
type: String,
default: '',
required: true
}
})
const form = ref({
id:null,
email: "",
gender: null,
phone: "",
businessId: "",
manicuristName: "",
employment_date: "",
specialties: "",
rating: null,
salary: null,
manicuristAvatar: ""
})
const formRef = ref(null);
const initFormData = async (id) => {
const res = await axios.get("manicurist/queryById", {manicuristId: id})
form.value = res.data.data;
// console.log(res.data.data)
}
watch(
() => props.id,
() => {
// console.log("id=" + props.id);
let id = props.id;
initFormData(id)
}
)
//
const emits = defineEmits(['update:modelValue', 'initEmployeeList'])
const handleClose = () => {
console.log("xxx")
//
emits('update:modelValue', false)
}
const handleConfirm = () => {
formRef.value.validate(async (valid) => {
if (valid) {
let result = await axios.post("manicurist/update", form.value)
console.log(result.data.data)
let data = result.data;
if (data.code == 0) {
ElMessage.success("执行成功!");
formRef.value.resetFields();
emits("initEmployeeList");
handleClose();
} else {
ElMessage.error(data.description);
}
} else {
console.log("fail")
return false
}
})
}
</script>
<style scoped>
</style>

208
src/views/home/index.vue Normal file
View File

@ -0,0 +1,208 @@
<template>
<div class="home">
甲情甲意流水展示
数据展示
</div>
<div style="display: flex" v-for="(item,index) in tableData" :key="index">
<div style="flex: 1">
<div id="t" style="height: 300px;width: 400px;border: 1px solid #ebebeb;border-radius: 2px;">
<el-form
:model="item"
label-width="100px"
style="height: auto"
>
<el-form-item label="门店头像" prop="businessAvatar">
<img :src="item.businessAvatar" width="50" height="50"/>
</el-form-item>
<el-form-item label="门店名" prop="businessName">
{{item.businessName}}
</el-form-item>
<el-form-item label="店主名" prop="shopkeeper">
{{item.shopkeeper}}
</el-form-item>
<el-form-item label="店铺详细地址" prop="address">
{{item.address}}
</el-form-item>
<el-form-item label="银行卡号" prop="bankCard">
{{item.bankCard}}
</el-form-item>
<el-form-item label="营业时间段" prop="startBusiness">
{{item.startBusiness}}~~{{item.endBusiness}}
</el-form-item>
</el-form>
</div>
</div>
<div style="flex: 1" >
<div id="main" style="height: 300px;width: 400px;border: 1px solid #ebebeb;border-radius: 2px;"></div>
</div>
<div style="flex: 1" >
<div id="main1" style="height: 300px;width: 400px;border: 1px solid #ebebeb;border-radius: 2px;"></div>
</div>
</div>
<!-- <el-pagination-->
<!-- v-model:currentPage="formPage.current"-->
<!-- v-model:page-size="formPage.pageSize"-->
<!-- :page-sizes="[10, 20, 30, 40,50]"-->
<!-- layout="total, sizes, prev, pager, next, jumper"-->
<!-- :total="total"-->
<!-- @size-change="handleSizeChange"-->
<!-- @current-change="handleCurrentChange"-->
<!-- />-->
</template>
<script setup>
import axios from "@/util/axios";
import * as echarts from "echarts";
import {onMounted, ref} from 'vue'
const total=ref(0)
const tableData =ref([])
const formPage=ref({
current:1,
pageSize:1
})
const initBusinessList=async()=>{
console.log(formPage.value)
const res=await axios.post("business/list/page",formPage.value);
console.log(res.data.data.records)
tableData.value=res.data.data.records;
total.value=res.data.data.records.length;
setTimeout(() => {hei();shi();}, 500)
}
initBusinessList();
const handleSizeChange = (pageSize) => {
formPage.value.current=1;
formPage.value.pageSize=pageSize;
initBusinessList();
}
const handleCurrentChange = (pageNum) => {
formPage.value.current=pageNum;
initBusinessList();
}
//
// onMounted(async () => {
// setTimeout(() => {hei();shi();}, 500)
// })
// onMounted(async () => {
// setTimeout(() => {hei();shi();}, 500)
// })
const hei=()=>{
var chartDom = document.getElementById('main1');
var myChart = echarts.init(chartDom);
var option;
option = {
tooltip: {
trigger: 'item'
},
legend: {
top: '5%',
left: 'center'
},
series: [
{
name: 'Access From',
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: 40,
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: [
{ value: 1048, name: 'Search Engine' },
{ value: 735, name: 'Direct' },
{ value: 580, name: 'Email' },
{ value: 484, name: 'Union Ads' },
{ value: 300, name: 'Video Ads' }
]
}
]
};
option && myChart.setOption(option);
}
const shi=()=>{
var chartDom = document.getElementById('main');
var myChart = echarts.init(chartDom);
var option;
option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: [
{
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
axisTick: {
alignWithLabel: true
}
}
],
yAxis: [
{
type: 'value'
}
],
series: [
{
name: 'Direct',
type: 'bar',
barWidth: '60%',
data: [10, 52, 200, 334, 390, 330, 220]
}
]
};
option && myChart.setOption(option);
}
</script>
<style lang="scss" scoped>
.home {
padding: 40px;
font-size: 30px;
font-weight: bold;
}
</style>

View File

@ -0,0 +1,18 @@
<template>
<div class="footer">
Copyright © 2024-* 甲情甲意 版权所有一切归你所有&nbsp;&nbsp;
<!-- <a href="http://www.java1234.vip" target="_blank">www.java1234.vip</a>-->
<!-- https://www.baidu.com/-->
<a href="https://www.baidu.com" target="_blank">www.baidu.com</a>
</div>
</template>
<script>
</script>
<style lang="scss" scoped>
.footer{
padding: 20px;
display: flex;
align-items: center;
}
</style>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,56 @@
<template>
<el-breadcrumb separator="/">
<el-breadcrumb-item v-for="(item,index) in breadcrumbList" :key="index">
<span class="redirect" v-if="item.name=='详情'||item.name=='商品管理'" @click="handleRedirectBack(item.name)">返回上一级</span>
<span class="no-redirect" v-else-if="index==breadcrumbList.length-1" >{{ item.name }}</span>
<span class="redirect" v-else @click="handleRedirect(item.path)">{{ item.name }}</span>
<!-- <span class="redirect" v-else @click="handleRedirectBack">返回上一级</span>-->
</el-breadcrumb-item>
</el-breadcrumb>
</template>
<script setup>
import {ref, watch} from 'vue'
import {useRoute, useRouter} from 'vue-router'
const route = useRoute();
const router = useRouter();
// console.log(route.matched)
const breadcrumbList = ref([]);
const handleRedirectBack=(name)=>{
if (name=='详情')
{
router.push('/employee')
}else{
router.push('/business')
}
}
const handleRedirect = (path) => {
router.push('/home')
}
const initBreadcrumbList = () => {
breadcrumbList.value = route.matched;
}
watch(route, () => {
initBreadcrumbList();
}, {deep: true, immediate: true})
</script>
<style lang="scss" scoped>
.no-redirect {
cursor: text;
}
.redirect {
color: #666;
font-weight: 600;
cursor: pointer;
&:hover {
color: #304156
}
}
</style>

102
src/views/layout/index.vue Normal file
View File

@ -0,0 +1,102 @@
<!--<template>-->
<!-- <div class="app-wrapper">-->
<!-- <el-container>-->
<!-- <el-aside width="200px" class="sidebar-container"><Menu/></el-aside>-->
<!-- <el-container>-->
<!-- <el-header>Header</el-header>-->
<!-- <el-main>Main</el-main>-->
<!-- </el-container>-->
<!-- </el-container>-->
<!-- </div>-->
<!--</template>-->
<!--<script setup>-->
<!--import Menu from "@/views/layout/menu/index.vue";-->
<!--import Breadcrumb from "@/views/layout/header/breadcrumb.vue";-->
<!--</script>-->
<!--<style scoped lang="scss">-->
<!--.app-wrapper {-->
<!-- position: relative;-->
<!-- width: 100%;-->
<!-- height: 100%;-->
<!--}-->
<!--.sidebar-container {-->
<!-- background-color: #2d3a4b;-->
<!-- height: 100%;-->
<!--}-->
<!--::v-deep .el-container{-->
<!-- height: 100%;-->
<!--}-->
<!--</style>-->
<template>
<div class="app-wrapper">
<el-container>
<el-aside width="200px" class="sidebar-container"><Menu/></el-aside>
<el-container>
<el-header>
<div class="navbar">
<Breadcrumb/>
<div class="navbar-right">
<Avatar/>
</div>
</div>
</el-header>
<el-main>
<router-view/>
</el-main>
<el-footer><Footer/></el-footer>
</el-container>
</el-container>
</div>
</template>
<script setup>
import Menu from '@/views/layout/menu'
import Breadcrumb from '@/views/layout/header/breadcrumb'
import Avatar from '@/views/layout/header/avatar'
import Footer from "@/views/layout/footer/index"
import modifyPassword from '@/views/modifyPassword/index'
import order from '@/views/order/index'
import smallType from '@/views/smallType/index.vue'
import bigType from '@/views/bigType/index.vue'
</script>
<style lang="scss" scoped>
.app-wrapper {
position: relative;
width: 100%;
height: 100%;
}
.navbar {
width: 100%;
height: 60px;
overflow: hidden;
background-color: #fff;
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
padding: 0 16px;
display: flex;
align-items: center;
box-sizing: border-box;
position: relative;
}
.navbar-right {
flex: 1;
display: flex;
align-items: center;
justify-content: flex-end;
}
:deep(.el-header){
padding: 0px;
}
.sidebar-container {
background-color: #2d3a4b;
height: 100%;
}
:deep(.el-container){
height: 100%;
}
</style>

View File

@ -0,0 +1,86 @@
<template>
<el-menu
active-text-color="#ffd04b"
background-color="#2d3a4b"
class="el-menu-vertical-demo"
default-active="home"
text-color="#fff"
router
>
<el-menu-item index="welcome">
<el-icon><home-filled /></el-icon>
<span>Welcome</span>
</el-menu-item>
<el-menu-item index="home">
<el-icon><home-filled /></el-icon>
<span>首页</span>
</el-menu-item>
<!-- <el-menu-item index="display">-->
<!-- <el-icon><tickets /></el-icon>-->
<!-- <span>数据展示</span>-->
<!-- </el-menu-item>-->
<el-menu-item index="user">
<el-icon><user /></el-icon>
<span>用户管理</span>
</el-menu-item>
<el-sub-menu index="3">
<template #title >
<el-icon><management /></el-icon>
<span>商家管理</span>
</template>
<el-menu-item index="business">
<el-icon><management /></el-icon>
<span>商家</span>
</el-menu-item>
<el-menu-item index="product">
<el-icon><management /></el-icon>
<span>商品管理</span>
</el-menu-item>
</el-sub-menu>
<el-menu-item index="employee">
<el-icon><tickets /></el-icon>
<span>美甲师管理</span>
</el-menu-item>
<el-menu-item index="order">
<el-icon><tickets /></el-icon>
<span>订单管理</span>
</el-menu-item>
<el-sub-menu index="11">
<template #title >
<el-icon><management /></el-icon>
<span>系统管理</span>
</template>
<el-menu-item index="personal">
<el-icon><House /></el-icon>
<span>个人中心</span>
</el-menu-item>
<el-menu-item index="modifyPassword">
<el-icon><edit /></el-icon>
<span>修改密码</span>
</el-menu-item>
<el-menu-item >
<el-icon @click="logout"><switch-button /></el-icon>
<span @click="logout">安全退出</span>
</el-menu-item>
</el-sub-menu>
</el-menu>
</template>
<script setup>
import
{HomeFilled,User,Tickets,Goods,DocumentAdd,Management,Setting,Edit,SwitchButton,House}
from '@element-plus/icons-vue'
import { useStore } from 'vuex'
import axios from "@/util/axios";
import {ElMessage} from "element-plus";
const store=useStore();
const logout=async ()=>{
store.dispatch('logout')
let res= await axios.post("/user/logout")
ElMessage.warning("退出成功");
//console.log(res)
}
</script>
<style lang="scss" scoped>
</style>

276
src/views/login/index.vue Normal file
View File

@ -0,0 +1,276 @@
<template>
<div class="login-container">
<el-form
ref="formRef"
:model="form"
:rules="rules"
class="login-form">
<div class="title-container">
<h3 class="title">甲情甲意管理平台</h3>
</div>
<el-form-item prop="userAccount">
<!-- <el-icon :size="20" class="svg-container">-->
<!-- <User/>-->
<!-- </el-icon>-->
<svg-icon icon="user" class="svg-container"></svg-icon>
<el-input v-model="form.userAccount" placeholder="请输入用户名..."></el-input>
</el-form-item>
<el-form-item prop="userPassword">
<!-- <el-icon :size="20" class="svg-container">-->
<!-- <lock/>-->
<!-- </el-icon>-->
<svg-icon icon="password" class="svg-container"></svg-icon>
<el-input v-model="form.userPassword" placeholder="请输入密码..."
type="password" />
</el-form-item>
<el-button type="primary" class="login-button" @click="handleLogin">登录</el-button>
</el-form>
</div>
</template>
<script setup>
import {ref} from 'vue'
import {Lock, User} from '@element-plus/icons-vue'
import axios, {request} from "axios";
import axiosUtil from '@/util/axios'
import {ElMessage} from "element-plus";
import router from "@/router";
const form = ref({
userAccount: 'root',
userPassword: '12345678'
});
const rules = ref({
userAccount: [
{
required: true,
message: '请输入用户名',
trigger: 'blur'
}
],
userPassword: [
{
required: true,
message: '请输入密码',
trigger: 'blur'
}
]
})
const formRef = ref(null);
const handleLogin = () => {
formRef.value.validate(async (valid) => {
if (valid) {
try {
let result = await axiosUtil.post("user/login", form.value);
let data = result.data;
console.log(result)
// const value = response.data;
// // Vue使
// this.sessionValue = value;
// console.log("宿"+data.value)
// console.log(""+JSON.stringify(data))
if (data.code == 0) {
// console.log(form.value)
ElMessage.success("登录成功");
window.sessionStorage.setItem("token",JSON.stringify(form.value));
window.sessionStorage.setItem("USER_LOGIN_STATE", JSON.stringify(data.data));
router.push('/home');
} else {
ElMessage.error(data.description);
}
} catch (err) {
console.log("error:" + err);
ElMessage.error("系统运行出错,请联系管理员");
}
}else {
console.log("验证失败");
}
})
}
</script>
<style lang="scss" scoped>
$bg: #2d3a4b;
$dark_gray: #889aa4;
$light_gray: #eee;
$cursor: #fff;
.login-container {
min-height: 100%;
width: 100%;
background-color: #ece0e3;
overflow: hidden;
.login-form {
box-shadow: 10px 10px 5px #888888;
position: relative;
background-color: #f8f3f4;
width: 520px;
border-radius: 20px;
// border: 1px solid red;
max-width: 100%;
padding: 40px 35px 40px;
margin: 140px auto;
overflow: hidden;
::v-deep .el-form-item {
border: 1px solid rgba(255, 255, 255, 0.1);
background: rgba(0, 0, 0, 0.1);
border-radius: 5px;
color: #454545;
}
::v-deep .el-form-item__content {
color: #454545;
background: rgba(0, 0, 0, 0.1);
}
::v-deep .el-input__wrapper {
display: block;
color: #9a9a9a;
background: #fff;
box-shadow: none;
}
::v-deep .el-input {
display: inline-block;
background: #fff;
height: 47px;
width: 85%;
input {
background-color: #ffffff;
border: 0px;
-webkit-appearance: none;
border-radius: 0px;
padding: 12px 5px 12px 15px;
color: #c8c4c5;
height: 47px;
caret-color: $cursor;
}
}
.login-button {
width: 100%;
box-sizing: border-box;
border-radius: 10px;
border: 0px;
background-color: #de868f;
}
}
.tips {
font-size: 16px;
line-height: 28px;
color: #fff;
margin-bottom: 10px;
span {
&:first-of-type {
margin-right: 16px;
}
}
}
.svg-container {
padding: 6px 5px 6px 15px;
color: $dark_gray;
vertical-align: middle;
display: inline-block;
}
.title-container {
position: relative;
.title {
font-size: 26px;
color: black;
margin: 0px auto 40px auto;
text-align: center;
font-weight: bold;
}
::v-deep .lang-select {
position: absolute;
top: 4px;
right: 0;
background-color: white;
font-size: 22px;
padding: 4px;
border-radius: 4px;
cursor: pointer;
}
}
.show-pwd {
// position: absolute;
// right: 10px;
// top: 7px;
font-size: 16px;
color: $dark_gray;
cursor: pointer;
user-select: none;
}
}
</style>
<!--<template>-->
<!--</template>-->
<!--<style>-->
<!--</style>-->
<!--<script>-->
<!--const t=[]-->
<!--axios.post(url, {-->
<!-- 参数名1: 参数值1,-->
<!-- 参数名2: 参数值2-->
<!--}).then(res => {-->
<!-- // res.data-->
<!-- // -->
<!-- // .then.catch-->
<!--})-->
<!--</script>-->
<!--<template>-->
<!-- <table cellspacing="0px" cellpadding="10px">-->
<!-- <tr>-->
<!-- <td>openID</td>-->
<!-- <td>shopID</td>-->
<!-- <td>totalAmout</td>-->
<!-- <td>orderID</td>-->
<!-- <td>note</td>-->
<!-- </tr>-->
<!-- <tr v-for="book in books" :key="book.orderID">-->
<!-- <td>{{ book.openID }}</td>-->
<!-- <td>{{ book.shopID }}</td>-->
<!-- <td>{{ book.totalAmout }}</td>-->
<!-- <td>{{ book.orderID }}</td>-->
<!-- <td>{{ book.note }}</td>-->
<!-- </tr>-->
<!-- </table>-->
<!-- <a href="http://localhost:5657/alipay/pay?subject=我是⭐&traceNo=124&totalAmount=8888&alipayTraceNo=1000">xixi</a>-->
<!--</template>-->
<!--<script setup>-->
<!--import axios from 'axios'-->
<!--import {ref} from "vue";-->
<!--let books =ref([]);-->
<!--// axios.get(-->
<!--// 'http://localhost:5657/alipay/pay?subject=&traceNo=124&totalAmount=8888&alipayTraceNo=1000')-->
<!--//-->
<!--// .then(response => {-->
<!--//-->
<!--//-->
<!--// console.log(response.data)-->
<!--//-->
<!--// })-->
<!--//-->
<!--// .catch(error => {-->
<!--//-->
<!--// console.log(error)-->
<!--//-->
<!--// })-->
<!--</script>-->

View File

@ -0,0 +1,111 @@
<template>
<el-card>
<el-form
ref="formRef"
:model="form"
:rules="rules"
label-width="150px"
>
<el-form-item label="用户名:" prop="username">
<el-input v-model="username" disabled/>
</el-form-item>
<el-form-item label="原密码:" prop="userPassword">
<el-input v-model="form.userPassword" type="password" show-password/>
</el-form-item>
<el-form-item label="新密码:" prop="newPassword">
<el-input v-model="form.newPassword" type="password" show-password/>
</el-form-item>
<el-form-item label="确认新密码:" prop="checkNewPassword">
<el-input v-model="form.checkNewPassword" type="password" show-password/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">确认修改
</el-button
>
</el-form-item>
</el-form>
</el-card>
</template>
<script setup>
import {ref} from 'vue'
import axios from "@/util/axios";
import {ElMessage} from "element-plus";
import { useStore } from 'vuex'
const store=useStore();
const username=ref()
const password=ref()
const form = ref({
userPassword: "",
newPassword: "",
checkNewPassword: ""
})
const rules = ref({
newPassword: [
{
required: true,
message: '请输入新密码'
}
],
newPassword2: [
{
required: true,
message: '请输入确认新密码'
}
],
})
const formRef = ref(null);
const initFormData = () => {
let userInfoJson = window.sessionStorage.getItem("USER_LOGIN_STATE");
//console.log(JSON.parse(userInfoJson).username)
username.value = JSON.parse(userInfoJson).username;
let pwd = window.sessionStorage.getItem("token");
password.value=JSON.parse(pwd).userPassword;
//console.log(JSON.parse(pwd).userPassword)
// form.value.userPassword = JSON.parse(pwd).userPassword;
// console.log(form.value.userPassword)
}
initFormData();
const onSubmit = () => {
formRef.value.validate(async (valid) => {
if (valid) {
//console.log("success")
if (form.value.userPassword !== password.value) {
ElMessage.error("原密码错误!");
} else if (form.value.newPassword !== form.value.checkNewPassword) {
ElMessage.error("确认新密码错误!");
} else {
try {
let result = await axios.post("user/password/update", form.value)
let data = result.data;
console.log(result.request)
if (data.code == 0) {
ElMessage.success("密码修改成功,重新登录后生效!");
formRef.value.resetFields();
} else {
ElMessage.error(data.description);
}
} catch (err) {
console.log(err)
ElMessage.error("系统运行出错,请联系管理员!");
}
}
} else {
console.log("fail")
return false
}
store.dispatch('logout')
let res= await axios.post("/user/logout")
console.log(res)
})
}
</script>
<style lang="scss" scoped>
.el-input {
width: 300px;
}
</style>

View File

@ -0,0 +1,55 @@
<template>
<el-dialog
model-value="dialogVisible"
title="订单详情"
width="40%"
@close="handleClose">
<el-table
:data="tableData"stripe style="width:100%">
<el-table-column prop="goodspic" label="商品图片" width="200">
<template v-slot="scope">
<img width="80" height="80" :src="getServerUrl()+'/image/product/'+scope.row.goodspic"/>
</template>
</el-table-column>
<el-table-column prop="goodsname" label="商品名称" ></el-table-column>
<el-table-column prop="goodsprice" label="商品价格" width="100"></el-table-column>
<el-table-column prop="goodsnumber" label="商品数量" width="100"></el-table-column>
</el-table>
</el-dialog>
</template>
<script setup>
import {defineEmits, defineProps, ref, watch} from "vue";
import axios,{getServerUrl} from "@/util/axios";
const props=defineProps({
id:{
type:Number,
default:-1,
required:true
}
})
watch(
()=>props.id,
()=>{
let id=props.id;
if (id != -1){
initOrderDetailData(id)
}
}
)
const tableData=ref(null);
const initOrderDetailData=async (id)=>{
const res=await axios.get("admin/orderDetial/list/"+id);
tableData.value=res.data.list;
console.log(res.data.list)
}
const emits=defineEmits(['update:modelValue'])
const handleClose=()=>{
emits('update:modelValue',false)
}
</script>
<style scoped>
</style>

177
src/views/order/index.vue Normal file
View File

@ -0,0 +1,177 @@
<template>
<el-card>
<el-row :gutter="20" class="header">
<el-col :span="7">
<el-input placeholder="请输入订单号..." clearable v-model="queryForm.orderNumber"></el-input>
</el-col>
<el-button type="button" :icon="Search" @click="initOrderList">搜索</el-button>
</el-row>
<el-table :data="tableData" stripe style="width: 100%" showOverflowTooltip>
<el-table-column prop="orderNumber" label="订单号" width="220" fixed/>
<el-table-column prop="userName" label="用户昵称" width="200"/>
<el-table-column prop="totalPrice" label="订单总价" width="100"/>
<el-table-column prop="paymentStatus" label="订单状态" width="100"/>
<el-table-column prop="createTime" label="订单创建日期" width="200"/>
<el-table-column prop="updateTime" label="订单支付日期" width="200"/>
<el-table-column prop="userName" label="收货人" width="80"/>
<el-table-column prop="phone" label="联系电话" width="150"/>
<el-table-column prop="notes" label="描述" width="400"/>
<!-- <el-table-column label="操作" width="300" fixed="right">-->
<!-- <template v-slot="scope">-->
<!-- <el-button type="success" @click="handleDialogValue(scope.row.id)">详情</el-button>-->
<!-- <el-button type="primary" @click="handleOrderStatus(scope.row.id,2)">发货</el-button>-->
<!-- <el-button type="primary" @click="handleOrderStatus(scope.row.id,3)">退货</el-button>-->
<!-- <el-button type="danger" :icon="Delete" @click="handleDelete(scope.row.id)"></el-button>-->
<!-- </template>-->
<!-- </el-table-column>-->
</el-table>
<el-pagination
v-model:currentPage="queryForm.current"
v-model:page-size="queryForm.pageSize"
:page-sizes="[10, 20, 30, 40,50]"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</el-card>
<Dialog v-model:="dialogVisible" :id="id"></Dialog>
</template>
<script setup>
import {Search,Delete} from '@element-plus/icons-vue'
import { ref} from 'vue'
import axios from "@/util/axios";
import Dialog from "@/views/order/dialog/index.vue";
import { ElMessage, ElMessageBox } from 'element-plus'
const handleDelete=(id)=>{
ElMessageBox.confirm(
'您确认要删除这个订单记录吗吗?',
'系统提示',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(async () => {
let res=await axios.get("admin/delete/"+id)
if (res.data.code==0){
ElMessage({
type: 'success',
message: '删除成功',
})
initOrderList();
}else{
ElMessage({
type: 'error',
message: res.data.msg,
})
}
})
.catch(() => {
})
}
const handleOrderStatus=(id,status)=>{
ElMessageBox.confirm(
'您确认要更新这个订单状态吗?',
'系统提示',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(async () => {
let res=await axios.post("admin/updateStatus",{id:id,status:status})
if (res.data.code==0){
ElMessage({
type: 'success',
message: '执行成功',
})
initOrderList();
}else{
ElMessage({
type: 'error',
message: res.data.msg,
})
}
})
.catch(() => {
})
}
const dialogVisible=ref(false)
const handleDialogValue=(orderId)=>{
id.value=orderId;
dialogVisible.value=true;
}
const id=ref(-1);
const queryForm=ref({
orderNumber:'',
current:1,
pageSize:10
})
const total=ref(0)
const tableData =ref([])
const initOrderList=async()=>{
const res=await axios.post("orders/list/page",queryForm.value);
//console.log(res.data.data)
// const temp=[];
// temp.push(res.data.data)
// console.log(res.data.data)
if(res.data.data){
tableData.value=res.data.data.records;
total.value=Number(res.data.data.total);
}
else{
ElMessage.error(res.data.description);
}
}
initOrderList();
const handleSizeChange = (pageSize) => {
queryForm.value.current=1;
queryForm.value.pageSize=pageSize;
initOrderList();
}
const handleCurrentChange = (current) => {
queryForm.value.current=current;
initOrderList();
}
const statusFormatter=(row)=>{
switch (row.status){
case 1:
return "待支付";
case 2:
return "待发货";
case 3:
return "退款/退货";
}
}
</script>
<style lang="scss" scoped>
.header{
padding-bottom: 16px;
box-sizing: border-box;
}
.el-pagination{
padding-top: 15px;
box-sizing: border-box;
}
</style>

View File

@ -0,0 +1,76 @@
<template>
<el-form
ref="formRef"
:model="form"
label-width="100px"
>
<el-form-item label="用户头像" prop="avatarUrl">
<img :src="form.avatarUrl" alt="空" width="50" height="50"/>
</el-form-item>
<el-form-item label="用户昵称" prop="username">
{{form.username}}
</el-form-item>
<el-form-item label="用户账号" prop="userAccount">
{{form.userAccount}}
</el-form-item>
<el-form-item label="性别" prop="gender">
<template v-if="form.gender==0"></template>
<template v-else></template>
</el-form-item>
<el-form-item label="电话" prop="phone">
{{form.phone}}
</el-form-item>
<el-form-item label="邮箱" prop="email">
{{form.email}}
</el-form-item>
<el-form-item label="用户状态" prop="userStatus">
<template v-if="form.userStatus==0">禁用</template>
<template v-else>启用</template>
</el-form-item>
<el-form-item label="用户角色" prop="userRole">
<template v-if="form.userRole===0">普通用户</template>
<template v-else-if="form.userRole===1">管理员</template>
<template v-else-if="form.userRole===2">商家</template>
<template v-else>美甲师</template>
</el-form-item>
</el-form>
</template>
<script setup>
import {ref, watch} from "vue";
import axios from "@/util/axios";
const form=ref({
avatarUrl: "",
createTime:"",
email:"",
gender:null,
isDelete:null,
openId:null,
phone:null,
unionId:null,
updateTime:"",
userAccount:"",
userPassword:"",
userRole:null,
userStatus:null,
username:""
})
const initFormData=()=>{
// console.log(ids)
let token=window.sessionStorage.getItem('USER_LOGIN_STATE');
// const temp=[]
// temp.push(token)
console.log(token)
form.value=JSON.parse(token);
}
initFormData();
</script>
<style scoped>
</style>

163
src/views/product/index.vue Normal file
View File

@ -0,0 +1,163 @@
<template>
<el-card>
<el-row :gutter="20" class="header" style="display: flex;align-items: center;
justify-content: center;" v-if="params.product!==undefined">
商家名称{{JSON.parse(params.product).businessName}}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
商家头像<img :src="JSON.parse(params.product).businessAvatar" width="50" height="50"/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
商家电话{{JSON.parse(params.product).businessPhone}}
</el-row>
<el-row :gutter="20" class="header" >
<el-col :span="7" >
<el-input placeholder="请输入商品ID..." clearable v-model="queryForm.id"></el-input>
</el-col>
<el-button type="primary" :icon="Search" @click="initProductList" >搜索</el-button>
<!-- <el-button type="primary" :icon="DocumentAdd"-->
<!-- @click="handleDialogValue()">添加商品</el-button>-->
</el-row>
<el-table :data="tableData" stripe style="width: 100%;">
<el-table-column prop="id" label="#ID" />
<el-table-column prop="commoditiesName" label="商品名称" />
<el-table-column prop="commoditiesImagecommoditiesImage" label="商品图片" >
<template v-slot="scope">
<img :src="scope.row.commoditiesImagecommoditiesImage"
width="50" height="50"/>
</template>
</el-table-column>
<el-table-column prop="commoditiesPrice" label="商品名称" />
<el-table-column prop="inventoryStatus" label="商品库存" />
<el-table-column prop="status" label="商品状态" />
</el-table>
<el-pagination
v-model:currentPage="queryForm.current"
v-model:page-size="queryForm.pageSize"
:page-sizes="[10, 20, 30, 40,50]"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</el-card>
<!-- <Dialog v-model="dialogVisible" :dialogVisible="dialogVisible"-->
<!-- :dialogTitle="dialogTitle" :id="id" @initProductList="initProductList"></Dialog>-->
</template>
<script setup>
import {Search,Delete,Edit,DocumentAdd} from '@element-plus/icons-vue'
import {ref, watch} from 'vue'
import axios,{getServerUrl} from "@/util/axios";
import { ElMessageBox,ElMessage } from 'element-plus'
import {useRoute} from "vue-router";
// import Dialog from './components/dialog'
let params=useRoute().query
const query=ref()
const total=ref(0)
const tableData =ref([])
const id=ref(-1)
const dialogTitle=ref('');
const dialogVisible=ref(false)
const queryForm=ref({
businessId: '',
id:'',
current:1,
pageSize:10
})
// const handleDialogValue=(productId)=>{
// if(productId){
// id.value=productId;
// dialogTitle.value=""
// }else{
// id.value=-1;
// dialogTitle.value=""
// }
// dialogVisible.value=true
// }
const initProductList=async()=>{
//console.log(JSON.parse(params.product).id)
if(params.product!=undefined){
queryForm.value.businessId=JSON.parse(params.product).id
}
const res=await axios.post("commodities/list/page/commodities",queryForm.value);
// console.log(res.data.data)
tableData.value=res.data.data.records;
total.value=Number(res.data.data.total);
}
initProductList();
watch(query,()=>{
// console.log(id.value)
initProductList();
},10000)
const handleSizeChange = (pageSize) => {
queryForm.value.current=1;
queryForm.value.pageSize=pageSize;
initProductList();
}
const handleCurrentChange = (current) => {
queryForm.value.current=current;
initProductList();
}
// const typeNameFormatter=(row)=>{
// // console.log(row)
// // let name=await axios.get('admin/smallType/'+row.typeid).then((res) => {
// // return res.data.smallType.name
// // });
// // row.smalltype=name;
// let name= axios.get('admin/smallType/'+row.typeid).then((res) => {
// return res.data.smallType.name
// });
// let t=name
// console.log(t.then())
// return t.then()
// }
// const typeNameFormatter=(row)=>{
// // console.log(row)
// let name= typeNameFormatter2(row)
// console.log(name)
// return name
// }
// const handleDelete=(id)=>{
// ElMessageBox.confirm(
// '',
// '',
// {
// confirmButtonText: '',
// cancelButtonText: '',
// type: 'warning',
// }
// )
// .then(async() => {
// let res=await axios.get('admin/product/delete/'+id)
// if(res.data.code==0){
// ElMessage({
// type: 'success',
// message: '',
// })
// initProductList();
// }else{
// ElMessage({
// type: 'error',
// message: res.data.msg,
// })
// }
// })
// .catch(() => {
// })
// }
</script>
<style lang="scss" scoped>
.header{
padding-bottom: 16px;
box-sizing: border-box;
}
.el-pagination{
padding-top: 15px;
box-sizing: border-box;
}
</style>

145
src/views/profile/index.vue Normal file
View File

@ -0,0 +1,145 @@
<template>
<el-form
ref="formRef"
:model="form"
label-width="100px"
style="display: flex; "
>
<!-- <el-form-item label="门店头像" prop="businessAvatar">-->
<!-- {{form.businessAvatar}}-->
<!-- </el-form-item>-->
<div style="flex: 1">
<el-form-item label="门店名" prop="businessName">
<el-input v-model="form.businessName" />
</el-form-item>
<el-form-item label="营业执照" prop="license">
<img :src="form.license" width="80" height="80"/>
</el-form-item>
<el-form-item label="店主名" prop="shopkeeper">
<el-input v-model="form.shopkeeper" />
</el-form-item>
<el-form-item label="门店手机号" prop="businessPhone">
<el-input v-model="form.businessPhone" />
</el-form-item>
</div>
<div style="flex: 1">
<el-form-item label="身份证" prop="frontIdCard" style="display: flex">
<div style="flex: 1">
<div>
<img :src="form.frontIdCard" width="80" height="80"/>
</div>
<div>
正面
</div>
</div>
<div style="flex: 1;">
<div>
<img :src="form.backIdCard" width="80" height="80"/>
</div>
<div>
反面
</div>
</div>
</el-form-item>
<!-- <el-form-item label="身份证反面" prop="backIdCard">-->
<!-- </el-form-item>-->
<el-form-item label="店铺地址" prop="address">
<el-input v-model="form.address" />
</el-form-item>
<el-form-item label="银行卡号" prop="bankCard">
<el-input v-model="form.bankCard" show-password/>
</el-form-item>
</div>
</el-form>
<div style="margin-top: 100px">
<div style="margin: 10px"> 是否审核通过</div>
<div style="display: flex;">
<div style="margin-left: 50px;" > <el-button type="danger" > 审核通过</el-button></div>
<div style="margin-left: 20px"><el-button type="danger" plain>审核不通过</el-button></div>
</div>
</div>
<!-- <el-form-item label="门店简介" prop="businessProfile">-->
<!-- {{form.businessProfile}}-->
<!-- </el-form-item>-->
<!-- <el-form-item label="商家相册" prop="businessImages">-->
<!-- {{form.businessImages}}-->
<!-- </el-form-item>-->
<!-- <el-form-item label="分类ID" prop="categoryId">-->
<!-- {{form.categoryId}}-->
<!-- </el-form-item>-->
<!-- <el-form-item label="开始营业时间" prop="startBusiness">-->
<!-- {{form.startBusiness}}-->
<!-- </el-form-item>-->
<!-- <el-form-item label="结束营业时间" prop="endBusiness">-->
<!-- {{form.endBusiness}}-->
<!-- </el-form-item>-->
<!-- <el-form-item label="创建时间" prop="createTime">-->
<!-- {{form.createTime}}-->
<!-- </el-form-item>-->
<!-- <template #footer>-->
<!--<span class="dialog-footer">-->
<!--<el-button @click="handleClose">取消</el-button>-->
<!--<el-button type="primary" @click="handleConfirm">确认</el-button>-->
<!--</span>-->
<!-- </template>-->
</template>
<script setup>
import {defineEmits, defineProps, ref, watch} from "vue";
import axios from "@/util/axios";
import { ElMessage } from "element-plus";
import {useRoute} from "vue-router"
let params=useRoute().query
const form=ref({
id:null,
address: "",
backIdCard: "",
bankCard: "",
businessAvatar: "",
businessImages: "",
businessName: "",
businessPhone: "",
businessProfile: "",
categoryId: null,
endBusiness: "",
frontIdCard: "",
license: "",
shopkeeper: "",
startBusiness: "",
createTime:""
})
const formRef=ref(null);
const initFormData=async()=>{
// console.log(params.id)
const res=await axios.post("business/list/page",{id: params.id})
console.log(res.data.data)
form.value=res.data.data.records[0];
//form.value=res.data.data;
}
initFormData();
// watch(
//
// ()=>{
// // console.log("id="+props.bussinessId);
//
// // console.log(id.value)
// initFormData(id)
// }
// )
</script>
<style scoped>
</style>

View File

@ -0,0 +1,138 @@
<template>
<el-dialog
model-value="dialogVisible"
:title="dialogTitle"
width="30%"
@close="handleClose"
>
<el-form
ref="formRef"
:model="form"
:rules="rules"
label-width="100px"
>
<el-form-item label="小类名称" prop="name">
<el-input v-model="form.name" />
</el-form-item>
<el-form-item label="所属大类">
<el-select v-model="form.bigtype.id" placeholder="请选择...">
<el-option
v-for="item in bigTypeSelectOptions"
:key="item.id"
:label="item.name"
:value="item.id"
>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="小类描述" prop="remark">
<el-input v-model="form.remark" type="textarea" :rows="4"/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="handleConfirm">确认</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import {defineEmits, defineProps, ref, watch} from "vue";
import axios from "@/util/axios";
import { ElMessage } from 'element-plus'
const tableData=ref([])
const bigTypeSelectOptions=ref([])
const initBigTypeSelectList=async()=>{
const res=await axios.get("admin/bigType/listAll");
bigTypeSelectOptions.value=res.data.bigTypeList;
}
initBigTypeSelectList();
const props=defineProps(
{
id:{
type:Number,
default:-1,
required:true
},
dialogTitle:{
type:String,
default:'',
required:true
},
dialogVisible:{
type:Boolean,
default:false,
required:true
}
}
)
const form=ref({
id:-1,
name:"",
remark:"",
bigtype:{
id:""
}
})
const rules=ref({
name:[
{ required: true, message: '请输入商品小类名称'}
],
remark:[
{ required: true, message: '请输入商品小类描述'}
]
})
const formRef=ref(null)
const initFormData=async(id)=>{
const res=await axios.get("admin/smallType/"+id);
form.value=res.data.smallType;
}
watch(
()=>props.dialogVisible,
()=>{
let id=props.id;
console.log("id="+id)
if(id!=-1){
initFormData(id)
}else{
form.value={
id:-1,
name:"",
remark:"",
bigtype:{
id:""
}
}
}
}
)
const emits=defineEmits(['update:modelValue','initSmallTypeList'])
const handleClose=()=>{
emits('update:modelValue',false)
}
const handleConfirm=()=>{
formRef.value.validate(async(valid)=>{
if(valid){
if(form.value.bigtype.id==""){
ElMessage.error("请选择商品大类");
return;
}
let result=await axios.post("admin/smallType/save",form.value);
let data=result.data;
if(data.code==0){
ElMessage.success("执行成功!")
formRef.value.resetFields();
emits("initSmallTypeList")
handleClose();
}else{
ElMessage.error(data.msg);
}
}else{
console.log("fail")
}
})
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,128 @@
<template>
<el-card>
<el-row :gutter="20" class="header">
<el-col :span="7">
<el-input placeholder="请输入商品小类名称..." clearable v-model="queryForm.query"></el-input>
</el-col>
<el-button type="button" :icon="Search" @click="initSmallTypeList">搜索
</el-button>
</el-row>
<el-table :data="tableData" stripe style="width: 100%">
<el-table-column prop="id" label="#ID" width="80" />
<el-table-column prop="name" label="商品小类名称" width="200" />
<el-table-column prop="bigType" label="所属大类" width="200"
:formatter="bigTypeNameFormatter"/>
<el-table-column prop="remark" label="商品小类描述"/>
<el-table-column prop="action" label="操作" width="300" >
<template v-slot="scope">
<el-button type="primary" :icon="Edit"
@click="handleDialogValue(scope.row.id)"></el-button>
<el-button type="danger" :icon="Delete"
@click="handleDelete(scope.row.id)"></el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
v-model:currentPage="queryForm.pageNum"
v-model:page-size="queryForm.pageSize"
:page-sizes="[10, 20, 30, 40,50]"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</el-card>
<!-- <Dialog v-model="dialogVisible" :id="id"></Dialog>-->
<Dialog v-model="dialogVisible" :dialogVisible="dialogVisible"
:dialogTitle="dialogTitle" :id="id" @initSmallTypeList="initSmallTypeList">
</Dialog>
</template>
<script setup>
import {Search,Delete,Edit} from '@element-plus/icons-vue'
import { ref } from 'vue'
import axios from "@/util/axios";
import { ElMessageBox,ElMessage } from 'element-plus'
import Dialog from '@/views/smallType/dialog/index.vue'
const queryForm=ref({
query:'',
pageNum:1,
pageSize:10
})
const dialogTitle=ref(null)
const total=ref(0)
const tableData =ref([])
const id=ref(-1)
const dialogVisible=ref(false)
// const handleDialogValue=(smallTypeId)=>{
// dialogVisible.value=true
// id.value=smallTypeId;
// }
const handleDialogValue=(smallTypeId)=>{
if(smallTypeId){
id.value=smallTypeId;
dialogTitle.value="商品小类修改"
}else{
id.value=-1;
dialogTitle.value="商品小类添加"
}
dialogVisible.value=true
}
const initSmallTypeList=async()=>{
const res=await axios.post("admin/smallType/list",queryForm.value);
tableData.value=res.data.smallTypeList;
total.value=res.data.total;
}
initSmallTypeList();
const handleSizeChange = (pageSize) => {
queryForm.value.pageNum=1;
queryForm.value.pageSize=pageSize;
initSmallTypeList();
}
const handleCurrentChange = (pageNum) => {
queryForm.value.pageNum=pageNum;
initSmallTypeList();
}
const bigTypeNameFormatter=(row)=>{
return row.bigtype.name
}
const handleDelete=(id)=>{
ElMessageBox.confirm(
'您确定要删除这条记录吗?',
'系统提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(async() => {
let res=await axios.get('admin/smallType/delete/'+id)
if(res.data.code==0){
ElMessage({
type: 'success',
message: '删除成功',
})
initSmallTypeList();
}else{
ElMessage({
type: 'error',
message: res.data.msg,
})
}
})
.catch(() => {
})
}
</script>
<style lang="scss" scoped>
.header{
padding-bottom: 16px;
box-sizing: border-box;
}
.el-pagination{
padding-top: 15px;
box-sizing: border-box;
}
</style>

278
src/views/test/index.vue Normal file
View File

@ -0,0 +1,278 @@
<template>
<div style="display: flex">
<div style="flex: 1">
<div id="echarRef" style="height: 300px;width: 400px;border: 1px solid #ebebeb;border-radius: 2px;"></div>
</div>
<div style="flex: 1">
<div id="main" style="height: 300px;width: 400px;border: 1px solid #ebebeb;border-radius: 2px;"></div>
</div>
</div>
<div style="display: flex">
<div style="flex: 1">
<div id="temp" style="height: 600px;width: 800px;border: 1px solid #ebebeb;border-radius: 2px;"></div>
</div>
<div style="flex: 1">
<div id="tanbai" style="height: 600px;width: 600px;border: 1px solid #ebebeb;border-radius: 2px;"></div>
</div>
</div>
</template>
<script setup>
import * as echarts from "echarts";
import { onMounted } from 'vue'
onMounted(async () => {
setTimeout(() => {hei()}, 1000)
setTimeout(() => {shi();}, 1000)
setTimeout(() => {temp();tanbai();}, 1000)
})
const hei = () => {
let myChart = echarts.init(document.getElementById("echarRef"));
myChart.setOption({
title: { text: "CPU使用率" },
tooltip: {},
xAxis: {
data: ["12-3", "12-4", "12-5", "12-6", "12-7", "12-8"],
},
yAxis: {},
series: [
{
name: "用户量",
type: "line",
data: [5, 20, 36, 10, 10, 20],
},
],
});
window.onresize = function () {//
myChart.resize();
};
}
const shi=()=>{
var chartDom = document.getElementById('main');
var myChart = echarts.init(chartDom);
var option;
option = {
title: { text: "订单柱状图" },
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
},
yAxis: {
type: 'value'
},
series: [
{
data: [120, 200, 150, 80, 70, 110, 130],
type: 'bar'
}
]
};
option && myChart.setOption(option);
}
const tanbai=()=>{
var chartDom = document.getElementById('tanbai');
var myChart = echarts.init(chartDom);
var option;
option = {
legend: {
top: 'bottom'
},
toolbox: {
show: true,
feature: {
mark: { show: true },
dataView: { show: true, readOnly: false },
restore: { show: true },
saveAsImage: { show: true }
}
},
series: [
{
name: 'Nightingale Chart',
type: 'pie',
radius: [50, 250],
center: ['50%', '50%'],
roseType: 'area',
itemStyle: {
borderRadius: 8
},
data: [
{ value: 40, name: 'rose 1' },
{ value: 38, name: 'rose 2' },
{ value: 32, name: 'rose 3' },
{ value: 30, name: 'rose 4' },
{ value: 28, name: 'rose 5' },
{ value: 26, name: 'rose 6' },
{ value: 22, name: 'rose 7' },
{ value: 18, name: 'rose 8' }
]
}
]
};
option && myChart.setOption(option);
}
const temp=()=>{
var app = {};
var chartDom = document.getElementById('temp');
var myChart = echarts.init(chartDom);
var option;
let xData = [];
let yData = [];
let data = [];
for (let y = 0; y < 10; y++) {
yData.push(y);
for (let x = 0; x < 10; x++) {
data.push([x, y, 10]);
}
}
for (let x = 0; x < 10; x++) {
xData.push(x);
}
const options = [
{
grid: {
left: 0,
right: 0,
top: 0,
bottom: 0
},
xAxis: {
show: false,
type: 'category',
data: xData
},
yAxis: {
show: false,
type: 'category',
data: yData
},
series: [
{
type: 'scatter',
data: data,
symbol: 'roundRect',
symbolKeepAspect: true,
universalTransition: true,
symbolSize: 50
}
]
},
{
series: [
{
type: 'scatter',
symbol: 'circle'
}
]
},
{
// heart
series: [
{
symbol:
'path://M23.6 2c-3.363 0-6.258 2.736-7.599 5.594-1.342-2.858-4.237-5.594-7.601-5.594-4.637 0-8.4 3.764-8.4 8.401 0 9.433 9.516 11.906 16.001 21.232 6.13-9.268 15.999-12.1 15.999-21.232 0-4.637-3.763-8.401-8.4-8.401z'
}
]
},
{
// happy
series: [
{
symbol:
'path://M16 0c-8.837 0-16 7.163-16 16s7.163 16 16 16 16-7.163 16-16-7.163-16-16-16zM22 8c1.105 0 2 1.343 2 3s-0.895 3-2 3-2-1.343-2-3 0.895-3 2-3zM10 8c1.105 0 2 1.343 2 3s-0.895 3-2 3-2-1.343-2-3 0.895-3 2-3zM16 28c-5.215 0-9.544-4.371-10-9.947 2.93 1.691 6.377 2.658 10 2.658s7.070-0.963 10-2.654c-0.455 5.576-4.785 9.942-10 9.942z'
}
]
},
{
// evil
series: [
{
symbol:
'path://M32 2c0-1.422-0.298-2.775-0.833-4-1.049 2.401-3.014 4.31-5.453 5.287-2.694-2.061-6.061-3.287-9.714-3.287s-7.021 1.226-9.714 3.287c-2.439-0.976-4.404-2.886-5.453-5.287-0.535 1.225-0.833 2.578-0.833 4 0 2.299 0.777 4.417 2.081 6.106-1.324 2.329-2.081 5.023-2.081 7.894 0 8.837 7.163 16 16 16s16-7.163 16-16c0-2.871-0.757-5.565-2.081-7.894 1.304-1.689 2.081-3.806 2.081-6.106zM18.003 11.891c0.064-1.483 1.413-2.467 2.55-3.036 1.086-0.543 2.16-0.814 2.205-0.826 0.536-0.134 1.079 0.192 1.213 0.728s-0.192 1.079-0.728 1.213c-0.551 0.139-1.204 0.379-1.779 0.667 0.333 0.357 0.537 0.836 0.537 1.363 0 1.105-0.895 2-2 2s-2-0.895-2-2c0-0.037 0.001-0.073 0.003-0.109zM8.030 8.758c0.134-0.536 0.677-0.862 1.213-0.728 0.045 0.011 1.119 0.283 2.205 0.826 1.137 0.569 2.486 1.553 2.55 3.036 0.002 0.036 0.003 0.072 0.003 0.109 0 1.105-0.895 2-2 2s-2-0.895-2-2c0-0.527 0.204-1.005 0.537-1.363-0.575-0.288-1.228-0.528-1.779-0.667-0.536-0.134-0.861-0.677-0.728-1.213zM16 26c-3.641 0-6.827-1.946-8.576-4.855l2.573-1.544c1.224 2.036 3.454 3.398 6.003 3.398s4.779-1.362 6.003-3.398l2.573 1.544c-1.749 2.908-4.935 4.855-8.576 4.855z'
}
]
},
{
// hipster
series: [
{
symbol:
'path://M16 0c-8.837 0-16 7.163-16 16s7.163 16 16 16 16-7.163 16-16-7.163-16-16-16zM22 8c1.105 0 2 0.895 2 2s-0.895 2-2 2-2-0.895-2-2 0.895-2 2-2zM10 8c1.105 0 2 0.895 2 2s-0.895 2-2 2-2-0.895-2-2 0.895-2 2-2zM16.994 21.23c-0.039-0.035-0.078-0.072-0.115-0.109-0.586-0.586-0.878-1.353-0.879-2.121-0 0.768-0.293 1.535-0.879 2.121-0.038 0.038-0.076 0.074-0.115 0.109-2.704 2.453-9.006-0.058-9.006-3.23 1.938 1.25 3.452 0.306 4.879-1.121 1.172-1.172 3.071-1.172 4.243 0 0.586 0.586 0.879 1.353 0.879 2.121 0-0.768 0.293-1.535 0.879-2.121 1.172-1.172 3.071-1.172 4.243 0 1.427 1.427 2.941 2.371 4.879 1.121 0 3.173-6.302 5.684-9.006 3.23z'
}
]
},
{
// shocked
series: [
{
symbol:
'path://M16 0c-8.837 0-16 7.163-16 16s7.163 16 16 16 16-7.163 16-16-7.163-16-16-16zM10 14c-1.105 0-2-1.343-2-3s0.895-3 2-3 2 1.343 2 3-0.895 3-2 3zM16 26c-2.209 0-4-1.791-4-4s1.791-4 4-4c2.209 0 4 1.791 4 4s-1.791 4-4 4zM22 14c-1.105 0-2-1.343-2-3s0.895-3 2-3 2 1.343 2 3-0.895 3-2 3z'
}
]
},
{
// pie chart
series: [
{
symbol:
'path://M14 18v-14c-7.732 0-14 6.268-14 14s6.268 14 14 14 14-6.268 14-14c0-2.251-0.532-4.378-1.476-6.262l-12.524 6.262zM28.524 7.738c-2.299-4.588-7.043-7.738-12.524-7.738v14l12.524-6.262z'
}
]
},
{
// users
series: [
{
symbol:
'path://M10.225 24.854c1.728-1.13 3.877-1.989 6.243-2.513-0.47-0.556-0.897-1.176-1.265-1.844-0.95-1.726-1.453-3.627-1.453-5.497 0-2.689 0-5.228 0.956-7.305 0.928-2.016 2.598-3.265 4.976-3.734-0.529-2.39-1.936-3.961-5.682-3.961-6 0-6 4.029-6 9 0 3.096 1.797 6.191 4 7.432v1.649c-6.784 0.555-12 3.888-12 7.918h8.719c0.454-0.403 0.956-0.787 1.506-1.146zM24 24.082v-1.649c2.203-1.241 4-4.337 4-7.432 0-4.971 0-9-6-9s-6 4.029-6 9c0 3.096 1.797 6.191 4 7.432v1.649c-6.784 0.555-12 3.888-12 7.918h28c0-4.030-5.216-7.364-12-7.918z'
}
]
},
{
// mug
series: [
{
symbol:
'path://M30 10h-6v-3c0-2.761-5.373-5-12-5s-12 2.239-12 5v20c0 2.761 5.373 5 12 5s12-2.239 12-5v-3h6c1.105 0 2-0.895 2-2v-10c0-1.105-0.895-2-2-2zM5.502 8.075c-1.156-0.381-1.857-0.789-2.232-1.075 0.375-0.286 1.075-0.694 2.232-1.075 1.811-0.597 4.118-0.925 6.498-0.925s4.688 0.329 6.498 0.925c1.156 0.381 1.857 0.789 2.232 1.075-0.375 0.286-1.076 0.694-2.232 1.075-1.811 0.597-4.118 0.925-6.498 0.925s-4.688-0.329-6.498-0.925zM28 20h-4v-6h4v6z'
}
]
},
{
// plane
series: [
{
symbol:
'path://M24 19.999l-5.713-5.713 13.713-10.286-4-4-17.141 6.858-5.397-5.397c-1.556-1.556-3.728-1.928-4.828-0.828s-0.727 3.273 0.828 4.828l5.396 5.396-6.858 17.143 4 4 10.287-13.715 5.713 5.713v7.999h4l2-6 6-2v-4l-7.999 0z'
}
]
}
];
let optionIndex = 0;
option = options[optionIndex];
setInterval(function () {
optionIndex = (optionIndex + 1) % options.length;
myChart.setOption(options[optionIndex]);
}, 700);
option && myChart.setOption(option);
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,110 @@
<template>
<el-dialog
model-value="dialogaddVisible"
:title="dialogTitle"
width="30%"
@close="handleClose"
>
<el-form
ref="formRef"
:model="form"
label-width="100px"
>
<el-form-item label="门店头像" prop="avatarUrl">
<el-upload list-type="picture-card" limit="1" :auto-upload="false" drag action="#">
<el-icon><Plus /></el-icon>
<template #tip>
<div class="el-upload__tip">
最多传一张
</div>
</template>
</el-upload>
</el-form-item>
<el-form-item label="用户昵称" prop="username">
<el-input v-model="form.username" />
</el-form-item>
<el-form-item label="用户账号" prop="userAccount">
<el-input v-model="form.userAccount" />
</el-form-item>
<el-form-item label="用户密码" prop="userPassword">
<el-input v-model="form.userPassword" show-password />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="handleConfirm">确认</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import {defineEmits, defineProps, ref, watch} from "vue";
import axios from "@/util/axios";
import { ElMessage } from "element-plus";
import { Delete, Download, Plus, ZoomIn } from '@element-plus/icons-vue'
const props=defineProps({
dialogTitle:{
type:String,
default:'',
required:true
}
})
const form=ref({
avatarUrl:'',
userAccount:'',
userPassword:'',
username:null
})
// const rules=ref({
// name: [
// {
// required: true,
// message: ''
// }
// ],
// remark: [
// {
// required: true,
// message: ''
// }
// ]
// })
const formRef=ref(null);
//
const emits=defineEmits(['update:modelValue','initUserList'])
const handleClose=()=>{
console.log("用户添加关闭xxx")
//
emits('update:modelValue',false)
}
const handleConfirm=()=>{
formRef.value.validate(async(valid)=>{
if(valid){
let result=await axios.post("user/add",form.value)
console.log(result.data)
let data=result.data;
if(data.code==0){
ElMessage.success("执行添加成功!");
formRef.value.resetFields();
emits("initUserList");
handleClose();
}else{
ElMessage.error(data.description);
}
}else{
console.log("fail")
return false
}
})
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,152 @@
<template>
<el-dialog
model-value="dialogVisible"
:title="dialogTitle"
width="30%"
@close="handleClose"
>
<el-form
ref="formRef"
:model="form"
:rules="rules"
label-width="100px"
>
<el-form-item label="用户头像" prop="avatarUrl">
<img :src="form.avatarUrl" alt="空" width="50" height="50"/>
</el-form-item>
<el-form-item label="用户昵称" prop="username">
{{form.username}}
</el-form-item>
<el-form-item label="用户账号" prop="userAccount">
{{form.userAccount}}
</el-form-item>
<el-form-item label="性别" prop="gender">
<template v-if="form.gender==0"></template>
<template v-else></template>
</el-form-item>
<el-form-item label="电话" prop="phone">
{{form.phone}}
</el-form-item>
<el-form-item label="邮箱" prop="email">
{{form.email}}
</el-form-item>
<el-form-item label="用户状态" prop="userStatus">
<template v-if="form.userStatus==0">禁用</template>
<template v-else>启用</template>
</el-form-item>
<el-form-item label="用户角色" prop="userRole">
<template v-if="form.userRole===0">普通用户</template>
<template v-else-if="form.userRole===1">管理员</template>
<template v-else-if="form.userRole===2">商家</template>
<template v-else>美甲师</template>
</el-form-item>
</el-form>
<!-- <template #footer>-->
<!--<span class="dialog-footer">-->
<!--<el-button @click="handleClose">取消</el-button>-->
<!--<el-button type="primary" @click="handleConfirm">确认</el-button>-->
<!--</span>-->
<!-- </template>-->
</el-dialog>
</template>
<script setup>
import {defineEmits, defineProps, ref, watch} from "vue";
import axios from "@/util/axios";
import { ElMessage } from "element-plus";
const props=defineProps({
id:{
type:String,
default:-1,
required:true
},
dialogTitle:{
type:String,
default:'',
required:true
}
})
const form=ref({
avatarUrl: "",
createTime:"",
email:"",
gender:null,
isDelete:null,
openId:null,
phone:null,
unionId:null,
updateTime:"",
userAccount:"",
userPassword:"",
userRole:null,
userStatus:null,
username:""
})
// const rules=ref({
// name: [
// {
// required: true,
// message: ''
// }
// ],
// remark: [
// {
// required: true,
// message: ''
// }
// ]
// })
const formRef=ref(null);
const initFormData=async(ids)=>{
// console.log(ids)
const res=await axios.get("user/getById",{id: ids})
// console.log(res.data.data)
form.value=res.data.data;
}
watch(
()=>props.id,
()=>{
//console.log("id="+props.id);
let ids=props.id;
initFormData(ids)
// }else{
// formRef.value.resetFields();
// form.value={
// id:-1,
// name:"",
// remark:""
// }
// }
}
)
//
const emits=defineEmits(['update:modelValue','initUserList'])
const handleClose=()=>{
console.log("xxx")
//
emits('update:modelValue',false)
}
// const handleConfirm=()=>{
// formRef.value.validate(async(valid)=>{
// if(valid){
// let result=await axios.post("admin/bigType/save",form.value)
// let data=result.data;
// if(data.code==0){
// ElMessage.success("");
// formRef.value.resetFields();
// emits("initUserList");
// handleClose();
// }else{
// ElMessage.error(data.msg);
// }
// }else{
// console.log("fail")
// return false
// }
// })
// }
</script>
<style scoped>
</style>

232
src/views/user/index.vue Normal file
View File

@ -0,0 +1,232 @@
<template>
<el-card>
<el-row :gutter="20" class="header">
<el-col :span="7">
<el-input placeholder="请输入用户昵称..." clearable v-model="queryForm.username"></el-input>
</el-col>
<el-button type="button" :icon="Search" @click="initUserList" >搜索</el-button>
<el-button type="primary" :icon="DocumentAdd" @click="handleAddDialogValue()" >添加用户</el-button>
</el-row>
<el-table :data="tableData" stripe style="width: 100%;" showOverflowTooltip>
<el-table-column fixed prop="id" label="#ID" width="80" />
<el-table-column prop="username" label="用户昵称" width="150" />
<el-table-column prop="avatarUrl" label="头像" width="200">
<template v-slot="scope">
<el-popover
placement="right-start"
:width="200"
trigger="hover"
:content="scope.row.avatarUrl"
>
<template #reference>
<img :src="scope.row.avatarUrl" width="50" height="50"/>
</template>
</el-popover>
</template>
</el-table-column>
<el-table-column prop="openId" label="openId" />
<el-table-column prop="phone" label="电话" width="120"/>
<el-table-column prop="email" label="email" width="200"/>
<el-table-column prop="userStatus" label="用户状态" width="200">
<template v-slot="scope">
<div v-if="scope.row.userStatus===0" style="color: red">
禁用
</div>
<div style="color: lawngreen" v-else>
启用
</div>
</template>
</el-table-column>
<el-table-column prop="userRole" label="用户角色" width="200">
<template v-slot="scope">
<div v-if="scope.row.userRole===0">普通用户</div>
<div v-else-if="scope.row.userRole===1">管理员</div>
<div v-else-if="scope.row.userRole===2">商家</div>
<div v-else>美甲师</div>
</template>
</el-table-column>
<el-table-column prop="createTime" label="注册日期" width="200">
<!-- <template v-slot="scope">-->
<!-- {{ parseTime(scope.row.createTime, "{y}-{m}-{d}") }}-->
<!-- </template>-->
</el-table-column>
<el-table-column prop="updateTime" label="最后登录日期" width="200"/>
<el-table-column prop="action" fixed="right" label="操作" min-width="170">
<template v-slot="scope" >
<el-button type="success" size="small" @click="handleDialogValue(scope.row.id)">
详情
</el-button>
<el-button type="primary" size="small" :icon="Edit" @click="handleUpdateDialogValue(scope.row.id)"></el-button>
<el-button type="danger" size="small" :icon="Delete" @click="handleDelete(scope.row.id)"></el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
v-model:currentPage="queryForm.current"
v-model:page-size="queryForm.pageSize"
:page-sizes="[10, 20, 30, 40,50]"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
<!-- {{total}}-->
</el-card>
<Dialog v-model="UdialogVisible" :id="id" :dialogTitle="dialogTitle"
@initUserList="initUserList"></Dialog>
<AddDialog v-model="UdialogaddVisible" :dialogTitle="dialogTitle"
@initUserList="initUserList"></AddDialog>
<UpdateDialog v-model="UdialogUpdateVisible" :id="id" :dialogTitle="dialogTitle"
@initUserList="initUserList"></UpdateDialog>
</template>
<script setup>
import {Search,Delete,Edit,DocumentAdd,Close,Check} from '@element-plus/icons-vue'
import {ref, watch} from 'vue'
import axios from "@/util/axios";
import Dialog from '@/views/user/dialog/index.vue'
import AddDialog from '@/views/user/addDialog/index.vue'
import UpdateDialog from '@/views/user/updateDialog/index.vue'
import { ElMessage,ElMessageBox } from "element-plus";
import {parseTime} from "element-plus/es/components/time-select/src/utils";
const queryForm=ref({
username: '',
current:1,
pageSize:10
})
const id=ref('')
const dialogTitle=ref('');
const UdialogaddVisible=ref(false)
const UdialogVisible=ref(false)
const UdialogUpdateVisible=ref(false)
// watch(queryForm.value.username,()=>{
// // console.log(query.value)
// initUserList();
// },10000)
const handleDialogValue=(ids)=>{
//console.log("bigTypeId="+userAccount)
// if(userAccount){
id.value=ids;
dialogTitle.value="用户详情"
// console.log("asdfasdfasd"+useraccount.value)
// }else{
// useraccount.value=-1;
// dialogTitle.value=""
// // console.log(dialogTitle.value)
// }
UdialogVisible.value=true
}
const handleUpdateDialogValue=(ids)=>{
// console.log(userAccount)
id.value=ids;
// console.log(userAccount.value)
dialogTitle.value="用户修改"
UdialogUpdateVisible.value=true
}
const handleAddDialogValue=()=>{
dialogTitle.value="用户添加"
UdialogaddVisible.value=true
}
//
const handleDelete=(id)=>{
// console.log(id)
ElMessageBox.confirm(
'您确定要删除这条记录吗?',
'系统提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(async() => {
let res=await axios.post('user/delete',{id:id})
if(res.data.code==0){
ElMessage({
type: 'success',
message: '删除成功',
})
initSmallTypeList();
}else{
ElMessage({
type: 'error',
message: res.data.description,
})
}
})
.catch(() => {
})
// initUserList();
}
const total=ref(0)
const tableData =ref([])
const initUserList=async()=>{
// // console.log(query)
// if (!query.value) {
// const res = await axios.post("user/list/page", queryForm.value);
// // console.log(res)
// tableData.value = res.data.data.records;
// //console.log(res.data.data)
// total.value = Number(res.data.data.total);
// }
// else{
// const res=await axios.get("user/getById", {id:query.value});
// // console.log(res.data.data)
// const temp=[];
// temp.push(res.data.data)
// tableData.value=temp;
// total.value=1;
// }
const res = await axios.post("user/list/page", queryForm.value);
// console.log(res.data.data.records)
if (res.data.data) {
tableData.value = res.data.data.records;
//console.log(res.data.data)
total.value = Number(res.data.data.total);
}
else{
ElMessage.error(res.data.description);
}
}
initUserList();
const handleSizeChange = (pageSize) => {
queryForm.value.current=1;
queryForm.value.pageSize=pageSize;
initUserList();
}
const handleCurrentChange = (current1) => {
queryForm.value.current=current1;
initUserList();
}
</script>
<style lang="scss" scoped>
.header{
padding-bottom: 16px;
box-sizing: border-box;
}
.el-pagination{
padding-top: 15px;
box-sizing: border-box;
}
</style>

View File

@ -0,0 +1,171 @@
<template>
<el-dialog
model-value="dialogUpdateVisible"
:title="dialogTitle"
width="30%"
@close="handleClose"
>
<el-form
ref="formRef"
:model="form"
label-width="100px"
>
<el-form-item label="用户头像" prop="avatarUrl">
<!-- {{ form.avatarUrl }}-->
<el-input v-model="form.avatarUrl"/>
</el-form-item>
<el-form-item label="用户昵称" prop="username">
<el-input v-model="form.username"/>
</el-form-item>
<el-form-item label="用户ID" prop="id">
<el-input v-model="form.id"/>
</el-form-item>
<el-form-item label="性别" prop="gender">
<template v-if="form.gender==0"></template>
<template v-else></template>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<el-radio-group v-model="form.gender">
<el-radio :value="0" size="default"></el-radio>
<el-radio :value="1" size="default"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="电话" prop="phone">
<el-input v-model="form.phone"/>
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="form.email"/>
</el-form-item>
<el-form-item label="用户状态" prop="userStatus">
<el-input v-model="form.userStatus"/>
</el-form-item>
<el-form-item label="用户角色" prop="userRole">
<!-- {{form.userRole}}-->
<!-- {{labelt}}-->
<!-- 展示由于v-model绑定出现v-model的form.userRole的值-->
<el-select
v-model="form.userRole"
size="default"
placeholder="用户角色"
style="width: 100%"
>
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="handleConfirm">确认</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import {defineEmits, defineProps, ref, watch} from "vue";
import axios from "@/util/axios";
import {ElMessage} from "element-plus";
const props = defineProps({
id: {
type: String,
default: -1,
required: true
},
dialogTitle: {
type: String,
default: '',
required: true
}
})
const labelt=ref('')
const form = ref({
id:null,
avatarUrl: "",
email: "",
gender: null,
phone: null,
userRole: null,
userStatus: null,
username: ""
})
const options = [
{
value: '',
label: '用户身份',
},
{
value: '0',
label: '普通用户',
},
{
value: '1',
label: '管理员',
},
{
value: '2',
label: '商家',
},
{
value: '3',
label: '美甲师',
},
]
const formRef = ref(null);
const initFormData = async (ids) => {
const res = await axios.get("user/getById", {id: ids})
form.value = res.data.data;
labelt.value=options[form.value.userRole].label
// console.log(options[form.value.userRole].label)
}
watch(
() => props.id,
() => {
// console.log("id=" + props.useraccount);
let ids = props.id;
initFormData(ids)
}
)
//
const emits = defineEmits(['update:modelValue', 'initUserList'])
const handleClose = () => {
console.log("xxx")
//
emits('update:modelValue', false)
}
const handleConfirm = () => {
formRef.value.validate(async (valid) => {
if (valid) {
let result = await axios.post("user/update", form.value)
let data = result.data;
if (data.code == 0) {
ElMessage.success("执行成功!");
formRef.value.resetFields();
emits("initUserList");
handleClose();
} else {
ElMessage.error(data.description);
}
} else {
console.log("fail")
return false
}
})
}
</script>
<style scoped>
</style>

474
src/views/welcome/index.vue Normal file
View File

@ -0,0 +1,474 @@
<template>
<el-row :gutter="16">
<el-col :span="6">
<div class="statistic-card">
<el-statistic :value="98500">
<template #title>
<div style="display: inline-flex; justify-self: right;align-items: center;width: 100%">
用户人数
<el-tooltip
effect="dark"
content="Number of users who logged into the product in one day"
placement="top"
>
<el-icon style="margin-left: 4px" :size="12">
<Warning />
</el-icon>
</el-tooltip>
<el-icon style="float: right" size="24"><Odometer /></el-icon>
</div>
</template>
</el-statistic>
<div class="statistic-footer">
<div class="footer-item">
<span class="green">
+24%
<el-icon>
<CaretTop />
</el-icon>
</span>
</div>
</div>
</div>
</el-col>
<el-col :span="6">
<div class="statistic-card">
<el-statistic :value="693700">
<template #title>
<div style="display: inline-flex; align-items: center">
商家人数
<el-tooltip
effect="dark"
content="Number of users who logged into the product in one month"
placement="top"
>
<el-icon style="margin-left: 4px" :size="12">
<Warning />
</el-icon>
</el-tooltip>
</div>
</template>
</el-statistic>
<div class="statistic-footer">
<div class="footer-item">
<span class="red">
+12%
<el-icon>
<CaretBottom />
</el-icon>
</span>
</div>
</div>
</div>
</el-col>
<el-col :span="6">
<div class="statistic-card">
<el-statistic :value="72000" title="New transactions today">
<template #title>
<div style="display: inline-flex; align-items: center">
订单数量
</div>
</template>
</el-statistic>
<div class="statistic-footer">
<div class="footer-item">
<span class="green">
+16%
<el-icon>
<CaretTop />
</el-icon>
</span>
</div>
</div>
</div>
</el-col>
<el-col :span="6">
<div class="statistic-card">
<el-statistic :value="72000" title="New transactions today">
<template #title>
<div style="display: inline-flex; align-items: center">
用户满意度
</div>
</template>
</el-statistic>
<div class="statistic-footer">
<div class="footer-item">
<span class="green">
+16%
<el-icon>
<CaretTop />
</el-icon>
</span>
</div>
</div>
</div>
</el-col>
</el-row>
<!-- ============================================================================ -->
<el-row :gutter="16">
<el-col :span="18">
<div class="statistic-card">
<div style="display: inline-flex; justify-self: right;align-items: center;width: 100%">
分析概览
</div>
<div class="statistic-footer">
<div class="footer-item">
<div id="main1" style="height: 320px;width: 780px;border: 1px solid #ebebeb;border-radius: 2px;"></div>
</div>
</div>
</div>
</el-col>
<el-col :span="6">
<div class="statistic-card">
<div style="display: inline-flex; justify-self: right;align-items: center;width: 100%">
每周展示
</div>
<div class="statistic-footer">
<div class="footer-item">
<div style="height: 320px;width: 220px;border: 1px solid #ebebeb;border-radius: 2px;">
<div class="common" >
<div style="flex: 8"><el-progress
:text-inside="true"
:stroke-width="15"
:percentage="100"
duration="30"
color="#26ce83"
striped
striped-flow
/></div>
<div style="flex: 1">周日</div>
</div>
<!-- ---------->
<div class="common">
<div style="flex: 8"><el-progress
:text-inside="true"
:stroke-width="15"
:percentage="100"
duration="30"
color="#26ce83"
striped
striped-flow
/></div>
<div style="flex: 1">周六</div>
</div>
<div class="common">
<div style="flex: 8"><el-progress
:text-inside="true"
:stroke-width="15"
:percentage="100"
duration="30"
color="#26ce83"
striped
striped-flow
/></div>
<div style="flex: 1">周五</div>
</div>
<div class="common">
<div style="flex: 8"><el-progress
:text-inside="true"
:stroke-width="15"
:percentage="100"
duration="30"
color="#26ce83"
striped
striped-flow
/></div>
<div style="flex: 1">周四</div>
</div>
<div class="common">
<div style="flex: 8"><el-progress
:text-inside="true"
:stroke-width="15"
:percentage="100"
duration="30"
color="#26ce83"
striped
striped-flow
/></div>
<div style="flex: 1">周三</div>
</div>
<div class="common">
<div style="flex: 8"><el-progress
:text-inside="true"
:stroke-width="15"
:percentage="100"
duration="30"
color="#26ce83"
striped
striped-flow
/></div>
<div style="flex: 1">周二</div>
</div>
<div class="common">
<div style="flex: 8"><el-progress
:text-inside="true"
:stroke-width="15"
:percentage="100"
duration="30"
color="#26ce83"
striped
striped-flow
/></div>
<div style="flex: 1">周一</div>
</div>
</div>
</div>
</div>
</div>
</el-col>
</el-row>
</template>
<script setup>
import {
ArrowRight,
Odometer,
CaretBottom,
CaretTop,
Warning,
} from '@element-plus/icons-vue'
import {onMounted} from "vue";
import * as echarts from 'echarts';
onMounted(async () => {
setTimeout(() => {hei();}, 500)
})
const hei=()=>{
var app = {};
var chartDom = document.getElementById('main1');
var myChart = echarts.init(chartDom);
var option;
const posList = [
'left',
'right',
'top',
'bottom',
'inside',
'insideTop',
'insideLeft',
'insideRight',
'insideBottom',
'insideTopLeft',
'insideTopRight',
'insideBottomLeft',
'insideBottomRight'
];
app.configParameters = {
rotate: {
min: -90,
max: 90
},
align: {
options: {
left: 'left',
center: 'center',
right: 'right'
}
},
verticalAlign: {
options: {
top: 'top',
middle: 'middle',
bottom: 'bottom'
}
},
position: {
options: posList.reduce(function (map, pos) {
map[pos] = pos;
return map;
}, {})
},
distance: {
min: 0,
max: 100
}
};
app.config = {
rotate: 90,
align: 'left',
verticalAlign: 'middle',
position: 'insideBottom',
distance: 15,
onChange: function () {
const labelOption = {
rotate: app.config.rotate,
align: app.config.align,
verticalAlign: app.config.verticalAlign,
position: app.config.position,
distance: app.config.distance
};
myChart.setOption({
series: [
{
label: labelOption
},
{
label: labelOption
},
{
label: labelOption
},
{
label: labelOption
}
]
});
}
};
const labelOption = {
show: true,
position: app.config.position,
distance: app.config.distance,
align: app.config.align,
verticalAlign: app.config.verticalAlign,
rotate: app.config.rotate,
formatter: '{c} {name|{a}}',
fontSize: 16,
rich: {
name: {}
}
};
option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
legend: {
data: ['Forest', 'Steppe', 'Desert', 'Wetland']
},
toolbox: {
show: true,
orient: 'vertical',
left: 'right',
top: 'center',
feature: {
mark: { show: true },
dataView: { show: true, readOnly: false },
magicType: { show: true, type: ['line', 'bar', 'stack'] },
restore: { show: true },
saveAsImage: { show: true }
}
},
xAxis: [
{
type: 'category',
axisTick: { show: false },
data: ['2012', '2013', '2014', '2015', '2016']
}
],
yAxis: [
{
type: 'value'
}
],
series: [
{
name: 'Forest',
type: 'bar',
barGap: 0,
label: labelOption,
emphasis: {
focus: 'series'
},
data: [320, 332, 301, 334, 390]
},
{
name: 'Steppe',
type: 'bar',
label: labelOption,
emphasis: {
focus: 'series'
},
data: [220, 182, 191, 234, 290]
},
{
name: 'Desert',
type: 'bar',
label: labelOption,
emphasis: {
focus: 'series'
},
data: [150, 232, 201, 154, 190]
},
{
name: 'Wetland',
type: 'bar',
label: labelOption,
emphasis: {
focus: 'series'
},
data: [98, 77, 101, 99, 40]
}
]
};
option && myChart.setOption(option);
}
</script>
<style scoped>
:global(h2#card-usage ~ .example .example-showcase) {
background-color: var(--el-fill-color) !important;
}
.common{
display: flex;
padding: 15px 0 15px;
}
.common_left{
flex: 8;
}
.common_right{
flex: 1;
}
.statistic-card {
padding: 20px;
border-radius: 4px;
margin-bottom: 20px;
background-color: #f0f2f5;
}
.statistic-footer {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
font-size: 12px;
color: var(--el-text-color-regular);
margin-top: 16px;
}
.statistic-footer .footer-item {
display: flex;
justify-content: space-between;
align-items: center;
}
.statistic-footer .footer-item span:last-child {
display: inline-flex;
align-items: center;
margin-left: 4px;
}
.green {
color: var(--el-color-success);
}
.red {
color: var(--el-color-error);
}
</style>

76
vue.config.js Normal file
View File

@ -0,0 +1,76 @@
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true
})
const webpack = require('webpack');
const path = require('path')
function resolve(dir) {
return path.join(__dirname, dir)
}
module.exports = {
transpileDependencies: true,
lintOnSave: false,
configureWebpack:{
resolve:{
alias:{
'@':resolve('src')
}
}
},
chainWebpack(config) {
// 设置 svg-sprite-loader
// config 为 webpack 配置对象
// config.module 表示创建一个具名规则,以后用来修改规则
config.module
// 规则
.rule('svg')
// 忽略
.exclude.add(resolve('src/icons'))
// 结束
.end()
// config.module 表示创建一个具名规则,以后用来修改规则
config.module
// 规则
.rule('icons')
// 正则,解析 .svg 格式文件
.test(/\.svg$/)
// 解析的文件
.include.add(resolve('src/icons'))
// 结束
.end()
// 新增了一个解析的loader
.use('svg-sprite-loader')
// 具体的loader
.loader('svg-sprite-loader')
// loader 的配置
.options({
symbolId: 'icon-[name]'
})
// 结束
.end()
config
.plugin('ignore')
.use(
new webpack.ContextReplacementPlugin(/moment[/\\]locale$/, /zh-cn$/)
)
config.module
.rule('icons')
.test(/\.svg$/)
.include.add(resolve('src/icons'))
.end()
.use('svg-sprite-loader')
.loader('svg-sprite-loader')
.options({
symbolId: 'icon-[name]'
})
.end()
}
}
//打包配置文件
module.exports = {
assetsDir: 'static',
parallel: false,
publicPath: './',
};