yyw提交
This commit is contained in:
parent
0ed1e71f00
commit
33a9f5c761
3
.browserslistrc
Normal file
3
.browserslistrc
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
> 1%
|
||||||
|
last 2 versions
|
||||||
|
not dead
|
||||||
19
.eslintrc.js
Normal file
19
.eslintrc.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
env: {
|
||||||
|
node: true
|
||||||
|
},
|
||||||
|
'extends': [
|
||||||
|
'plugin:vue/vue3-essential',
|
||||||
|
'eslint:recommended'
|
||||||
|
],
|
||||||
|
parserOptions: {
|
||||||
|
parser: 'babel-eslint'
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
// 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||||
|
// 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
|
||||||
|
'no-console': 'off',
|
||||||
|
'no-debugger': 'off'
|
||||||
|
}
|
||||||
|
}
|
||||||
27
.gitignore
vendored
27
.gitignore
vendored
@ -1,11 +1,22 @@
|
|||||||
# ---> Vue
|
.DS_Store
|
||||||
# gitignore template for Vue.js projects
|
node_modules
|
||||||
#
|
/dist
|
||||||
# Recommended template: Node.gitignore
|
|
||||||
|
|
||||||
# TODO: where does this rule come from?
|
# local env files
|
||||||
docs/_book
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
|
||||||
# TODO: where does this rule come from?
|
# Log files
|
||||||
test/
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
|||||||
14
babel.config.js
Normal file
14
babel.config.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// 项目在发布时需要用到的 babel 插件数组
|
||||||
|
const proPlugins = [];
|
||||||
|
// 如果当前是测试环境或者是生产环境,则使用去掉 console 的插件
|
||||||
|
if (process.env.NODE_ENV === 'test' || process.env.NODE_ENV === 'production') {
|
||||||
|
proPlugins.push('transform-remove-console');
|
||||||
|
}
|
||||||
|
module.exports = {
|
||||||
|
presets: [
|
||||||
|
'@vue/cli-plugin-babel/preset'
|
||||||
|
],
|
||||||
|
plugins:[
|
||||||
|
...proPlugins
|
||||||
|
]
|
||||||
|
}
|
||||||
14147
package-lock.json
generated
Normal file
14147
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
54
package.json
Normal file
54
package.json
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
{
|
||||||
|
"name": "e-commerce",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"serve": "vue-cli-service serve",
|
||||||
|
"build": "vue-cli-service build",
|
||||||
|
"lint": "vue-cli-service lint"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@element-plus/icons": "0.0.11",
|
||||||
|
"@wecom/jssdk": "^2.0.2",
|
||||||
|
"axios": "^0.24.0",
|
||||||
|
"bignumber.js": "^9.1.2",
|
||||||
|
"canvas": "^2.9.0",
|
||||||
|
"clipboard": "^2.0.11",
|
||||||
|
"core-js": "^3.6.5",
|
||||||
|
"dayjs": "^1.11.13",
|
||||||
|
"echarts": "^5.3.2",
|
||||||
|
"element-plus": "^2.8.4",
|
||||||
|
"file-saver": "^2.0.5",
|
||||||
|
"jquery": "^3.6.0",
|
||||||
|
"laravel-echo": "^1.11.7",
|
||||||
|
"moment": "^2.29.4",
|
||||||
|
"pusher-js": "^7.1.0-beta",
|
||||||
|
"sass": "^1.43.4",
|
||||||
|
"sortablejs": "^1.15.0",
|
||||||
|
"vue": "^3.0.0",
|
||||||
|
"vue-router": "^4.0.0-0",
|
||||||
|
"vue3-treeselect": "^0.1.10",
|
||||||
|
"vuex": "^4.1.0",
|
||||||
|
"wangeditor": "^4.7.9",
|
||||||
|
"xe-utils": "^3.5.7",
|
||||||
|
"xlsx": "^0.17.4",
|
||||||
|
"xlsx-style": "^0.8.13"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@arco-design/web-vue": "^2.55.2",
|
||||||
|
"@vue/cli-plugin-babel": "~4.5.0",
|
||||||
|
"@vue/cli-plugin-eslint": "~4.5.0",
|
||||||
|
"@vue/cli-plugin-router": "~4.5.0",
|
||||||
|
"@vue/cli-plugin-vuex": "~4.5.0",
|
||||||
|
"@vue/cli-service": "~4.5.0",
|
||||||
|
"@vue/compiler-sfc": "^3.0.0",
|
||||||
|
"babel-eslint": "^10.1.0",
|
||||||
|
"babel-plugin-transform-remove-console": "^6.9.4",
|
||||||
|
"eslint": "^6.7.2",
|
||||||
|
"eslint-plugin-vue": "^7.0.0",
|
||||||
|
"node-sass": "^4.14.1",
|
||||||
|
"sass-loader": "^8.0.2",
|
||||||
|
"script-loader": "^0.7.2",
|
||||||
|
"webpack": "^4.46.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
20
public/index.html
Normal file
20
public/index.html
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<!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">
|
||||||
|
<meta http-equiv="pragram" content="no-cache">
|
||||||
|
<meta http-equiv="cache-control" content="no-cache, no-store, must-revalidate">
|
||||||
|
<meta http-equiv="expires" content="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>
|
||||||
74
src/App.vue
Normal file
74
src/App.vue
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
<template>
|
||||||
|
<router-view />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script src="@/assets/icon/iconfont.js"></script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
#app {
|
||||||
|
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*解决element表格不对齐*/
|
||||||
|
body .el-table th.gutter {
|
||||||
|
display: table-cell !important;
|
||||||
|
}
|
||||||
|
.page-pagination {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: #fff;
|
||||||
|
padding-top: 15px;
|
||||||
|
}
|
||||||
|
.scrollbar-wrapper {
|
||||||
|
overflow-x: hidden !important;
|
||||||
|
}
|
||||||
|
/*解决element上传组件回显动画问题*/
|
||||||
|
body .el-upload-list__item {
|
||||||
|
transition: none !important;
|
||||||
|
}
|
||||||
|
.pageBox{
|
||||||
|
// height: 100%;
|
||||||
|
// overflow: auto;
|
||||||
|
padding: 15px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
body .el-message-box__title{
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
body .el-page-header__back{
|
||||||
|
border: 1px solid #D9D9D9;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 2px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 设置滚动条的样式 */
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 7px;
|
||||||
|
height: 7px;
|
||||||
|
}
|
||||||
|
/* 滚动槽 */
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
box-shadow: inset 0 0 5px rgba(0,0,0,0.2);
|
||||||
|
border-radius: 0;
|
||||||
|
background: rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
/* 滚动条滑块 */
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: inset 0 0 5px rgba(0,0,0,0.2);
|
||||||
|
background: rgba(0,0,0,0.2);
|
||||||
|
}
|
||||||
|
.vanMsg.el-message{
|
||||||
|
left: 60px;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
4
src/api/index.js
Normal file
4
src/api/index.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
const url = "http://shop.dev.chutang66.com"; // 测试
|
||||||
|
export default {
|
||||||
|
url,
|
||||||
|
};
|
||||||
157
src/api/request.js
Normal file
157
src/api/request.js
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
import axios from 'axios'
|
||||||
|
import router from '@/router'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
|
let showMsg = true;
|
||||||
|
function interceptRequest () {
|
||||||
|
axios.interceptors.request.use(config =>
|
||||||
|
{
|
||||||
|
config.headers['content-type'] = 'application/json'
|
||||||
|
config.headers.Authorization = localStorage.getItem('token') || ''
|
||||||
|
config.headers['Shop-Id'] = localStorage.getItem('shopId') || ''
|
||||||
|
|
||||||
|
// 登录接口、登出接口、刷新token接口不需要token
|
||||||
|
if (config.url.indexOf('/auth/refresh') >= 0 || config.url.indexOf('/auth/login') >= 0 || config.url.indexOf('/auth/logout') >= 0) {
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
let access_token = localStorage.getItem('token')
|
||||||
|
let saveTime = Number(localStorage.getItem('saveTime'))
|
||||||
|
let expires_in = Number(localStorage.getItem('expires_in'))
|
||||||
|
|
||||||
|
// 如果token已经过期
|
||||||
|
if((access_token && saveTime && expires_in && (Date.now() > saveTime + expires_in)) || !access_token) {
|
||||||
|
ElMessage('登录用户已过期,请重新登录')
|
||||||
|
localStorage.removeItem('token')
|
||||||
|
localStorage.removeItem('saveTime')
|
||||||
|
localStorage.removeItem('bind_wechat')
|
||||||
|
router.replace({
|
||||||
|
path: '/login'
|
||||||
|
})
|
||||||
|
Reflect.deleteProperty(config, 'headers')
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
}, err => {
|
||||||
|
return Promise.reject(err)
|
||||||
|
})
|
||||||
|
|
||||||
|
axios.interceptors.response.use(response => {
|
||||||
|
if (response.status === 200 || response.status === 201 || response.status === 204) {
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
// return response
|
||||||
|
}, err => {
|
||||||
|
return Promise.reject(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get方法,对应get请求
|
||||||
|
* @param {String} url [请求的url地址]
|
||||||
|
* @param {Object} params [请求时携带的参数]
|
||||||
|
*/
|
||||||
|
export function get (url = '', params = {}, responseType = '') {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
interceptRequest()
|
||||||
|
axios
|
||||||
|
.get(url, {
|
||||||
|
params: params,
|
||||||
|
responseType
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
resolve(res.data)
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
if (showMsg && err.response && err.response.data && err.response.data.message) {
|
||||||
|
ElMessage(err.response.data.message)
|
||||||
|
}
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* post方法,对应post请求
|
||||||
|
* @param {String} url [请求的url地址]
|
||||||
|
* @param {Object} data [请求时携带的参数]
|
||||||
|
* @param { string } requestType 请求类型 默认为post
|
||||||
|
*/
|
||||||
|
export function post (url = '', data = {}, requestType = 'post') {
|
||||||
|
requestType = requestType.toLowerCase()
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
interceptRequest()
|
||||||
|
let dataObj = {}
|
||||||
|
dataObj = requestType === 'delete' ? { data } : data
|
||||||
|
axios[requestType](url, { ...dataObj })
|
||||||
|
.then(res => {
|
||||||
|
resolve(res.data)
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
if (showMsg && err.response && err.response.data && err.response.data.message) {
|
||||||
|
ElMessage(err.response.data.message)
|
||||||
|
}
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* post方法,用于处理图片等文件流
|
||||||
|
* @param {String} url [请求的url地址]
|
||||||
|
* @param {Object} data [请求时携带的参数]
|
||||||
|
* @param { string } requestType 请求类型 默认为post
|
||||||
|
*/
|
||||||
|
export function postForm(url = '', data = {}, requestType = 'post') {
|
||||||
|
const formData = new FormData();
|
||||||
|
Object.keys(data).forEach(key => {
|
||||||
|
const item = data[key]
|
||||||
|
if (key === 'goods') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (Array.isArray(data[key])) {
|
||||||
|
if (key === 'specs') {
|
||||||
|
formData.append(key, JSON.stringify(item));
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (item.length) {
|
||||||
|
item.forEach( (j, index) => {
|
||||||
|
if (Object.prototype.toString.call(j) === '[object Object]') {
|
||||||
|
Object.keys(j).forEach(jkey => {
|
||||||
|
formData.append(`${key}[${index}][${jkey}]`, j[jkey]);
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
formData.append(`${key}[]`, j || '');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
formData.append(`${key}`, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (Object.prototype.toString.call(item) === '[object Object]') {
|
||||||
|
Object.keys(item).forEach(jkey => {
|
||||||
|
formData.append(`${key}[${jkey}]`, item[jkey]);
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
if(data[key] === 0) {
|
||||||
|
formData.append(key, data[key]);
|
||||||
|
} else {
|
||||||
|
formData.append(key, data[key] || '');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
interceptRequest()
|
||||||
|
axios[requestType](url, formData)
|
||||||
|
.then(res => {
|
||||||
|
resolve(res.data)
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
ElMessage({
|
||||||
|
type: 'warning',
|
||||||
|
message: err.response.data.message
|
||||||
|
})
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
16077
src/assets/citylist.js
Normal file
16077
src/assets/citylist.js
Normal file
File diff suppressed because it is too large
Load Diff
539
src/assets/icon/demo.css
Normal file
539
src/assets/icon/demo.css
Normal file
@ -0,0 +1,539 @@
|
|||||||
|
/* Logo 字体 */
|
||||||
|
@font-face {
|
||||||
|
font-family: "iconfont logo";
|
||||||
|
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
|
||||||
|
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
|
||||||
|
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
|
||||||
|
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
|
||||||
|
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
font-family: "iconfont logo";
|
||||||
|
font-size: 160px;
|
||||||
|
font-style: normal;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* tabs */
|
||||||
|
.nav-tabs {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tabs .nav-more {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
height: 42px;
|
||||||
|
line-height: 42px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tabs {
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tabs li {
|
||||||
|
cursor: pointer;
|
||||||
|
width: 100px;
|
||||||
|
height: 40px;
|
||||||
|
line-height: 40px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 16px;
|
||||||
|
border-bottom: 2px solid transparent;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
margin-bottom: -1px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#tabs .active {
|
||||||
|
border-bottom-color: #f00;
|
||||||
|
color: #222;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-container .content {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 页面布局 */
|
||||||
|
.main {
|
||||||
|
padding: 30px 100px;
|
||||||
|
width: 960px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main .logo {
|
||||||
|
color: #333;
|
||||||
|
text-align: left;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
line-height: 1;
|
||||||
|
height: 110px;
|
||||||
|
margin-top: -50px;
|
||||||
|
overflow: hidden;
|
||||||
|
*zoom: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main .logo a {
|
||||||
|
font-size: 160px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.helps {
|
||||||
|
margin-top: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.helps pre {
|
||||||
|
padding: 20px;
|
||||||
|
margin: 10px 0;
|
||||||
|
border: solid 1px #e7e1cd;
|
||||||
|
background-color: #fffdef;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon_lists {
|
||||||
|
width: 100% !important;
|
||||||
|
overflow: hidden;
|
||||||
|
*zoom: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon_lists li {
|
||||||
|
width: 100px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
margin-right: 20px;
|
||||||
|
text-align: center;
|
||||||
|
list-style: none !important;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon_lists li .code-name {
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon_lists .icon {
|
||||||
|
display: block;
|
||||||
|
height: 100px;
|
||||||
|
line-height: 100px;
|
||||||
|
font-size: 42px;
|
||||||
|
margin: 10px auto;
|
||||||
|
color: #333;
|
||||||
|
-webkit-transition: font-size 0.25s linear, width 0.25s linear;
|
||||||
|
-moz-transition: font-size 0.25s linear, width 0.25s linear;
|
||||||
|
transition: font-size 0.25s linear, width 0.25s linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon_lists .icon:hover {
|
||||||
|
font-size: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon_lists .svg-icon {
|
||||||
|
/* 通过设置 font-size 来改变图标大小 */
|
||||||
|
width: 1em;
|
||||||
|
/* 图标和文字相邻时,垂直对齐 */
|
||||||
|
vertical-align: -0.15em;
|
||||||
|
/* 通过设置 color 来改变 SVG 的颜色/fill */
|
||||||
|
fill: currentColor;
|
||||||
|
/* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
|
||||||
|
normalize.css 中也包含这行 */
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon_lists li .name,
|
||||||
|
.icon_lists li .code-name {
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* markdown 样式 */
|
||||||
|
.markdown {
|
||||||
|
color: #666;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight {
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown img {
|
||||||
|
vertical-align: middle;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown h1 {
|
||||||
|
color: #404040;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 40px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown h2,
|
||||||
|
.markdown h3,
|
||||||
|
.markdown h4,
|
||||||
|
.markdown h5,
|
||||||
|
.markdown h6 {
|
||||||
|
color: #404040;
|
||||||
|
margin: 1.6em 0 0.6em 0;
|
||||||
|
font-weight: 500;
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown h1 {
|
||||||
|
font-size: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown h2 {
|
||||||
|
font-size: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown h3 {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown h4 {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown h5 {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown h6 {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown hr {
|
||||||
|
height: 1px;
|
||||||
|
border: 0;
|
||||||
|
background: #e9e9e9;
|
||||||
|
margin: 16px 0;
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown p {
|
||||||
|
margin: 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown>p,
|
||||||
|
.markdown>blockquote,
|
||||||
|
.markdown>.highlight,
|
||||||
|
.markdown>ol,
|
||||||
|
.markdown>ul {
|
||||||
|
width: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown ul>li {
|
||||||
|
list-style: circle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown>ul li,
|
||||||
|
.markdown blockquote ul>li {
|
||||||
|
margin-left: 20px;
|
||||||
|
padding-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown>ul li p,
|
||||||
|
.markdown>ol li p {
|
||||||
|
margin: 0.6em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown ol>li {
|
||||||
|
list-style: decimal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown>ol li,
|
||||||
|
.markdown blockquote ol>li {
|
||||||
|
margin-left: 20px;
|
||||||
|
padding-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown code {
|
||||||
|
margin: 0 3px;
|
||||||
|
padding: 0 5px;
|
||||||
|
background: #eee;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown strong,
|
||||||
|
.markdown b {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown>table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-spacing: 0px;
|
||||||
|
empty-cells: show;
|
||||||
|
border: 1px solid #e9e9e9;
|
||||||
|
width: 95%;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown>table th {
|
||||||
|
white-space: nowrap;
|
||||||
|
color: #333;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown>table th,
|
||||||
|
.markdown>table td {
|
||||||
|
border: 1px solid #e9e9e9;
|
||||||
|
padding: 8px 16px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown>table th {
|
||||||
|
background: #F7F7F7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown blockquote {
|
||||||
|
font-size: 90%;
|
||||||
|
color: #999;
|
||||||
|
border-left: 4px solid #e9e9e9;
|
||||||
|
padding-left: 0.8em;
|
||||||
|
margin: 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown blockquote p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown .anchor {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown .waiting {
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown h1:hover .anchor,
|
||||||
|
.markdown h2:hover .anchor,
|
||||||
|
.markdown h3:hover .anchor,
|
||||||
|
.markdown h4:hover .anchor,
|
||||||
|
.markdown h5:hover .anchor,
|
||||||
|
.markdown h6:hover .anchor {
|
||||||
|
opacity: 1;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown>br,
|
||||||
|
.markdown>p>br {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.hljs {
|
||||||
|
display: block;
|
||||||
|
background: white;
|
||||||
|
padding: 0.5em;
|
||||||
|
color: #333333;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-comment,
|
||||||
|
.hljs-meta {
|
||||||
|
color: #969896;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-string,
|
||||||
|
.hljs-variable,
|
||||||
|
.hljs-template-variable,
|
||||||
|
.hljs-strong,
|
||||||
|
.hljs-emphasis,
|
||||||
|
.hljs-quote {
|
||||||
|
color: #df5000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-keyword,
|
||||||
|
.hljs-selector-tag,
|
||||||
|
.hljs-type {
|
||||||
|
color: #a71d5d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-literal,
|
||||||
|
.hljs-symbol,
|
||||||
|
.hljs-bullet,
|
||||||
|
.hljs-attribute {
|
||||||
|
color: #0086b3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-section,
|
||||||
|
.hljs-name {
|
||||||
|
color: #63a35c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-tag {
|
||||||
|
color: #333333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-title,
|
||||||
|
.hljs-attr,
|
||||||
|
.hljs-selector-id,
|
||||||
|
.hljs-selector-class,
|
||||||
|
.hljs-selector-attr,
|
||||||
|
.hljs-selector-pseudo {
|
||||||
|
color: #795da3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-addition {
|
||||||
|
color: #55a532;
|
||||||
|
background-color: #eaffea;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-deletion {
|
||||||
|
color: #bd2c00;
|
||||||
|
background-color: #ffecec;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-link {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 代码高亮 */
|
||||||
|
/* PrismJS 1.15.0
|
||||||
|
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
|
||||||
|
/**
|
||||||
|
* prism.js default theme for JavaScript, CSS and HTML
|
||||||
|
* Based on dabblet (http://dabblet.com)
|
||||||
|
* @author Lea Verou
|
||||||
|
*/
|
||||||
|
code[class*="language-"],
|
||||||
|
pre[class*="language-"] {
|
||||||
|
color: black;
|
||||||
|
background: none;
|
||||||
|
text-shadow: 0 1px white;
|
||||||
|
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||||
|
text-align: left;
|
||||||
|
white-space: pre;
|
||||||
|
word-spacing: normal;
|
||||||
|
word-break: normal;
|
||||||
|
word-wrap: normal;
|
||||||
|
line-height: 1.5;
|
||||||
|
|
||||||
|
-moz-tab-size: 4;
|
||||||
|
-o-tab-size: 4;
|
||||||
|
tab-size: 4;
|
||||||
|
|
||||||
|
-webkit-hyphens: none;
|
||||||
|
-moz-hyphens: none;
|
||||||
|
-ms-hyphens: none;
|
||||||
|
hyphens: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre[class*="language-"]::-moz-selection,
|
||||||
|
pre[class*="language-"] ::-moz-selection,
|
||||||
|
code[class*="language-"]::-moz-selection,
|
||||||
|
code[class*="language-"] ::-moz-selection {
|
||||||
|
text-shadow: none;
|
||||||
|
background: #b3d4fc;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre[class*="language-"]::selection,
|
||||||
|
pre[class*="language-"] ::selection,
|
||||||
|
code[class*="language-"]::selection,
|
||||||
|
code[class*="language-"] ::selection {
|
||||||
|
text-shadow: none;
|
||||||
|
background: #b3d4fc;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
|
||||||
|
code[class*="language-"],
|
||||||
|
pre[class*="language-"] {
|
||||||
|
text-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Code blocks */
|
||||||
|
pre[class*="language-"] {
|
||||||
|
padding: 1em;
|
||||||
|
margin: .5em 0;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
:not(pre)>code[class*="language-"],
|
||||||
|
pre[class*="language-"] {
|
||||||
|
background: #f5f2f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Inline code */
|
||||||
|
:not(pre)>code[class*="language-"] {
|
||||||
|
padding: .1em;
|
||||||
|
border-radius: .3em;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.comment,
|
||||||
|
.token.prolog,
|
||||||
|
.token.doctype,
|
||||||
|
.token.cdata {
|
||||||
|
color: slategray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.punctuation {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.namespace {
|
||||||
|
opacity: .7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.property,
|
||||||
|
.token.tag,
|
||||||
|
.token.boolean,
|
||||||
|
.token.number,
|
||||||
|
.token.constant,
|
||||||
|
.token.symbol,
|
||||||
|
.token.deleted {
|
||||||
|
color: #905;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.selector,
|
||||||
|
.token.attr-name,
|
||||||
|
.token.string,
|
||||||
|
.token.char,
|
||||||
|
.token.builtin,
|
||||||
|
.token.inserted {
|
||||||
|
color: #690;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.operator,
|
||||||
|
.token.entity,
|
||||||
|
.token.url,
|
||||||
|
.language-css .token.string,
|
||||||
|
.style .token.string {
|
||||||
|
color: #9a6e3a;
|
||||||
|
background: hsla(0, 0%, 100%, .5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.atrule,
|
||||||
|
.token.attr-value,
|
||||||
|
.token.keyword {
|
||||||
|
color: #07a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.function,
|
||||||
|
.token.class-name {
|
||||||
|
color: #DD4A68;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.regex,
|
||||||
|
.token.important,
|
||||||
|
.token.variable {
|
||||||
|
color: #e90;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.important,
|
||||||
|
.token.bold {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.italic {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.entity {
|
||||||
|
cursor: help;
|
||||||
|
}
|
||||||
441
src/assets/icon/demo_index.html
Normal file
441
src/assets/icon/demo_index.html
Normal file
@ -0,0 +1,441 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8"/>
|
||||||
|
<title>iconfont Demo</title>
|
||||||
|
<link rel="shortcut icon" href="//img.alicdn.com/imgextra/i4/O1CN01Z5paLz1O0zuCC7osS_!!6000000001644-55-tps-83-82.svg" type="image/x-icon"/>
|
||||||
|
<link rel="icon" type="image/svg+xml" href="//img.alicdn.com/imgextra/i4/O1CN01Z5paLz1O0zuCC7osS_!!6000000001644-55-tps-83-82.svg"/>
|
||||||
|
<link rel="stylesheet" href="https://g.alicdn.com/thx/cube/1.3.2/cube.min.css">
|
||||||
|
<link rel="stylesheet" href="demo.css">
|
||||||
|
<link rel="stylesheet" href="iconfont.css">
|
||||||
|
<script src="iconfont.js"></script>
|
||||||
|
<!-- jQuery -->
|
||||||
|
<script src="https://a1.alicdn.com/oss/uploads/2018/12/26/7bfddb60-08e8-11e9-9b04-53e73bb6408b.js"></script>
|
||||||
|
<!-- 代码高亮 -->
|
||||||
|
<script src="https://a1.alicdn.com/oss/uploads/2018/12/26/a3f714d0-08e6-11e9-8a15-ebf944d7534c.js"></script>
|
||||||
|
<style>
|
||||||
|
.main .logo {
|
||||||
|
margin-top: 0;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main .logo a {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main .logo .sub-title {
|
||||||
|
margin-left: 0.5em;
|
||||||
|
font-size: 22px;
|
||||||
|
color: #fff;
|
||||||
|
background: linear-gradient(-45deg, #3967FF, #B500FE);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="main">
|
||||||
|
<h1 class="logo"><a href="https://www.iconfont.cn/" title="iconfont 首页" target="_blank">
|
||||||
|
<img width="200" src="https://img.alicdn.com/imgextra/i3/O1CN01Mn65HV1FfSEzR6DKv_!!6000000000514-55-tps-228-59.svg">
|
||||||
|
|
||||||
|
</a></h1>
|
||||||
|
<div class="nav-tabs">
|
||||||
|
<ul id="tabs" class="dib-box">
|
||||||
|
<li class="dib active"><span>Unicode</span></li>
|
||||||
|
<li class="dib"><span>Font class</span></li>
|
||||||
|
<li class="dib"><span>Symbol</span></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<a href="https://www.iconfont.cn/manage/index?manage_type=myprojects&projectId=4742714" target="_blank" class="nav-more">查看项目</a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="tab-container">
|
||||||
|
<div class="content unicode" style="display: block;">
|
||||||
|
<ul class="icon_lists dib-box">
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">组合</div>
|
||||||
|
<div class="code-name">&#xe679;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">首页</div>
|
||||||
|
<div class="code-name">&#xe77e;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">商品列表</div>
|
||||||
|
<div class="code-name">&#xe685;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">导出</div>
|
||||||
|
<div class="code-name">&#xe854;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">导入</div>
|
||||||
|
<div class="code-name">&#xe664;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">品牌</div>
|
||||||
|
<div class="code-name">&#xe722;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">采购单</div>
|
||||||
|
<div class="code-name">&#xe61d;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">仓库</div>
|
||||||
|
<div class="code-name">&#xe668;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">供应商</div>
|
||||||
|
<div class="code-name">&#xe611;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">商品规格</div>
|
||||||
|
<div class="code-name">&#xe65b;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">出入库</div>
|
||||||
|
<div class="code-name">&#xe600;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
<div class="article markdown">
|
||||||
|
<h2 id="unicode-">Unicode 引用</h2>
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<p>Unicode 是字体在网页端最原始的应用方式,特点是:</p>
|
||||||
|
<ul>
|
||||||
|
<li>支持按字体的方式去动态调整图标大小,颜色等等。</li>
|
||||||
|
<li>默认情况下不支持多色,直接添加多色图标会自动去色。</li>
|
||||||
|
</ul>
|
||||||
|
<blockquote>
|
||||||
|
<p>注意:新版 iconfont 支持两种方式引用多色图标:SVG symbol 引用方式和彩色字体图标模式。(使用彩色字体图标需要在「编辑项目」中开启「彩色」选项后并重新生成。)</p>
|
||||||
|
</blockquote>
|
||||||
|
<p>Unicode 使用步骤如下:</p>
|
||||||
|
<h3 id="-font-face">第一步:拷贝项目下面生成的 <code>@font-face</code></h3>
|
||||||
|
<pre><code class="language-css"
|
||||||
|
>@font-face {
|
||||||
|
font-family: 'iconfont';
|
||||||
|
src: url('iconfont.woff2?t=1731383577261') format('woff2'),
|
||||||
|
url('iconfont.woff?t=1731383577261') format('woff'),
|
||||||
|
url('iconfont.ttf?t=1731383577261') format('truetype');
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
|
||||||
|
<pre><code class="language-css"
|
||||||
|
>.iconfont {
|
||||||
|
font-family: "iconfont" !important;
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
<h3 id="-">第三步:挑选相应图标并获取字体编码,应用于页面</h3>
|
||||||
|
<pre>
|
||||||
|
<code class="language-html"
|
||||||
|
><span class="iconfont">&#x33;</span>
|
||||||
|
</code></pre>
|
||||||
|
<blockquote>
|
||||||
|
<p>"iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。</p>
|
||||||
|
</blockquote>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="content font-class">
|
||||||
|
<ul class="icon_lists dib-box">
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont icon-cc"></span>
|
||||||
|
<div class="name">
|
||||||
|
组合
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.icon-cc
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont icon-home"></span>
|
||||||
|
<div class="name">
|
||||||
|
首页
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.icon-home
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont icon-list"></span>
|
||||||
|
<div class="name">
|
||||||
|
商品列表
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.icon-list
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont icon-daochu"></span>
|
||||||
|
<div class="name">
|
||||||
|
导出
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.icon-daochu
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont icon-daoru"></span>
|
||||||
|
<div class="name">
|
||||||
|
导入
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.icon-daoru
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont icon-brand"></span>
|
||||||
|
<div class="name">
|
||||||
|
品牌
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.icon-brand
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont icon-caigou"></span>
|
||||||
|
<div class="name">
|
||||||
|
采购单
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.icon-caigou
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont icon-cangku"></span>
|
||||||
|
<div class="name">
|
||||||
|
仓库
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.icon-cangku
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont icon-supplier"></span>
|
||||||
|
<div class="name">
|
||||||
|
供应商
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.icon-supplier
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont icon-sku"></span>
|
||||||
|
<div class="name">
|
||||||
|
商品规格
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.icon-sku
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont icon-churuku"></span>
|
||||||
|
<div class="name">
|
||||||
|
出入库
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.icon-churuku
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
<div class="article markdown">
|
||||||
|
<h2 id="font-class-">font-class 引用</h2>
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<p>font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。</p>
|
||||||
|
<p>与 Unicode 使用方式相比,具有如下特点:</p>
|
||||||
|
<ul>
|
||||||
|
<li>相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。</li>
|
||||||
|
<li>因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。</li>
|
||||||
|
</ul>
|
||||||
|
<p>使用步骤如下:</p>
|
||||||
|
<h3 id="-fontclass-">第一步:引入项目下面生成的 fontclass 代码:</h3>
|
||||||
|
<pre><code class="language-html"><link rel="stylesheet" href="./iconfont.css">
|
||||||
|
</code></pre>
|
||||||
|
<h3 id="-">第二步:挑选相应图标并获取类名,应用于页面:</h3>
|
||||||
|
<pre><code class="language-html"><span class="iconfont icon-xxx"></span>
|
||||||
|
</code></pre>
|
||||||
|
<blockquote>
|
||||||
|
<p>"
|
||||||
|
iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。</p>
|
||||||
|
</blockquote>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="content symbol">
|
||||||
|
<ul class="icon_lists dib-box">
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#icon-cc"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">组合</div>
|
||||||
|
<div class="code-name">#icon-cc</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#icon-home"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">首页</div>
|
||||||
|
<div class="code-name">#icon-home</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#icon-list"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">商品列表</div>
|
||||||
|
<div class="code-name">#icon-list</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#icon-daochu"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">导出</div>
|
||||||
|
<div class="code-name">#icon-daochu</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#icon-daoru"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">导入</div>
|
||||||
|
<div class="code-name">#icon-daoru</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#icon-brand"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">品牌</div>
|
||||||
|
<div class="code-name">#icon-brand</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#icon-caigou"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">采购单</div>
|
||||||
|
<div class="code-name">#icon-caigou</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#icon-cangku"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">仓库</div>
|
||||||
|
<div class="code-name">#icon-cangku</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#icon-supplier"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">供应商</div>
|
||||||
|
<div class="code-name">#icon-supplier</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#icon-sku"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">商品规格</div>
|
||||||
|
<div class="code-name">#icon-sku</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#icon-churuku"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">出入库</div>
|
||||||
|
<div class="code-name">#icon-churuku</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
<div class="article markdown">
|
||||||
|
<h2 id="symbol-">Symbol 引用</h2>
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<p>这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇<a href="">文章</a>
|
||||||
|
这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:</p>
|
||||||
|
<ul>
|
||||||
|
<li>支持多色图标了,不再受单色限制。</li>
|
||||||
|
<li>通过一些技巧,支持像字体那样,通过 <code>font-size</code>, <code>color</code> 来调整样式。</li>
|
||||||
|
<li>兼容性较差,支持 IE9+,及现代浏览器。</li>
|
||||||
|
<li>浏览器渲染 SVG 的性能一般,还不如 png。</li>
|
||||||
|
</ul>
|
||||||
|
<p>使用步骤如下:</p>
|
||||||
|
<h3 id="-symbol-">第一步:引入项目下面生成的 symbol 代码:</h3>
|
||||||
|
<pre><code class="language-html"><script src="./iconfont.js"></script>
|
||||||
|
</code></pre>
|
||||||
|
<h3 id="-css-">第二步:加入通用 CSS 代码(引入一次就行):</h3>
|
||||||
|
<pre><code class="language-html"><style>
|
||||||
|
.icon {
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
vertical-align: -0.15em;
|
||||||
|
fill: currentColor;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</code></pre>
|
||||||
|
<h3 id="-">第三步:挑选相应图标并获取类名,应用于页面:</h3>
|
||||||
|
<pre><code class="language-html"><svg class="icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#icon-xxx"></use>
|
||||||
|
</svg>
|
||||||
|
</code></pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
$(document).ready(function () {
|
||||||
|
$('.tab-container .content:first').show()
|
||||||
|
|
||||||
|
$('#tabs li').click(function (e) {
|
||||||
|
var tabContent = $('.tab-container .content')
|
||||||
|
var index = $(this).index()
|
||||||
|
|
||||||
|
if ($(this).hasClass('active')) {
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
$('#tabs li').removeClass('active')
|
||||||
|
$(this).addClass('active')
|
||||||
|
|
||||||
|
tabContent.hide().eq(index).fadeIn()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
59
src/assets/icon/iconfont.css
Normal file
59
src/assets/icon/iconfont.css
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: "iconfont"; /* Project id 4742714 */
|
||||||
|
src: url('iconfont.woff2?t=1731383577261') format('woff2'),
|
||||||
|
url('iconfont.woff?t=1731383577261') format('woff'),
|
||||||
|
url('iconfont.ttf?t=1731383577261') format('truetype');
|
||||||
|
}
|
||||||
|
|
||||||
|
.iconfont {
|
||||||
|
font-family: "iconfont" !important;
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-cc:before {
|
||||||
|
content: "\e679";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-home:before {
|
||||||
|
content: "\e77e";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-list:before {
|
||||||
|
content: "\e685";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-daochu:before {
|
||||||
|
content: "\e854";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-daoru:before {
|
||||||
|
content: "\e664";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-brand:before {
|
||||||
|
content: "\e722";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-caigou:before {
|
||||||
|
content: "\e61d";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-cangku:before {
|
||||||
|
content: "\e668";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-supplier:before {
|
||||||
|
content: "\e611";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-sku:before {
|
||||||
|
content: "\e65b";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-churuku:before {
|
||||||
|
content: "\e600";
|
||||||
|
}
|
||||||
|
|
||||||
1
src/assets/icon/iconfont.js
Normal file
1
src/assets/icon/iconfont.js
Normal file
File diff suppressed because one or more lines are too long
86
src/assets/icon/iconfont.json
Normal file
86
src/assets/icon/iconfont.json
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
{
|
||||||
|
"id": "4742714",
|
||||||
|
"name": "erp自研",
|
||||||
|
"font_family": "iconfont",
|
||||||
|
"css_prefix_text": "icon-",
|
||||||
|
"description": "",
|
||||||
|
"glyphs": [
|
||||||
|
{
|
||||||
|
"icon_id": "836390",
|
||||||
|
"name": "组合",
|
||||||
|
"font_class": "cc",
|
||||||
|
"unicode": "e679",
|
||||||
|
"unicode_decimal": 59001
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "28892148",
|
||||||
|
"name": "首页",
|
||||||
|
"font_class": "home",
|
||||||
|
"unicode": "e77e",
|
||||||
|
"unicode_decimal": 59262
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "8478985",
|
||||||
|
"name": "商品列表",
|
||||||
|
"font_class": "list",
|
||||||
|
"unicode": "e685",
|
||||||
|
"unicode_decimal": 59013
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "16398959",
|
||||||
|
"name": "导出",
|
||||||
|
"font_class": "daochu",
|
||||||
|
"unicode": "e854",
|
||||||
|
"unicode_decimal": 59476
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "17882700",
|
||||||
|
"name": "导入",
|
||||||
|
"font_class": "daoru",
|
||||||
|
"unicode": "e664",
|
||||||
|
"unicode_decimal": 58980
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "680982",
|
||||||
|
"name": "品牌",
|
||||||
|
"font_class": "brand",
|
||||||
|
"unicode": "e722",
|
||||||
|
"unicode_decimal": 59170
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "1614433",
|
||||||
|
"name": "采购单",
|
||||||
|
"font_class": "caigou",
|
||||||
|
"unicode": "e61d",
|
||||||
|
"unicode_decimal": 58909
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "1680702",
|
||||||
|
"name": "仓库",
|
||||||
|
"font_class": "cangku",
|
||||||
|
"unicode": "e668",
|
||||||
|
"unicode_decimal": 58984
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "1963493",
|
||||||
|
"name": "供应商",
|
||||||
|
"font_class": "supplier",
|
||||||
|
"unicode": "e611",
|
||||||
|
"unicode_decimal": 58897
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "10311371",
|
||||||
|
"name": "商品规格",
|
||||||
|
"font_class": "sku",
|
||||||
|
"unicode": "e65b",
|
||||||
|
"unicode_decimal": 58971
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "25073515",
|
||||||
|
"name": "出入库",
|
||||||
|
"font_class": "churuku",
|
||||||
|
"unicode": "e600",
|
||||||
|
"unicode_decimal": 58880
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
src/assets/icon/iconfont.ttf
Normal file
BIN
src/assets/icon/iconfont.ttf
Normal file
Binary file not shown.
BIN
src/assets/icon/iconfont.woff
Normal file
BIN
src/assets/icon/iconfont.woff
Normal file
Binary file not shown.
BIN
src/assets/icon/iconfont.woff2
Normal file
BIN
src/assets/icon/iconfont.woff2
Normal file
Binary file not shown.
BIN
src/assets/imgs/404.jpg
Normal file
BIN
src/assets/imgs/404.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 63 KiB |
19
src/assets/main.scss
Normal file
19
src/assets/main.scss
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
@forward 'element-plus/theme-chalk/src/common/var.scss' with (
|
||||||
|
$colors: (
|
||||||
|
'primary': (
|
||||||
|
'base': #3c8dbc,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
$table: (
|
||||||
|
'header-text-color': #333,
|
||||||
|
'text-color': #333,
|
||||||
|
),
|
||||||
|
$select: (
|
||||||
|
'disabled-color': #666,
|
||||||
|
),
|
||||||
|
$disabled: (
|
||||||
|
'text-color': #666,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
@use 'element-plus/theme-chalk/src/index.scss' as *;
|
||||||
35
src/components/Breadcrumb/index.vue
Normal file
35
src/components/Breadcrumb/index.vue
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<template>
|
||||||
|
<el-breadcrumb :separator="props.separator">
|
||||||
|
<el-breadcrumb-item
|
||||||
|
v-for="route of infoList"
|
||||||
|
:key="route.path"
|
||||||
|
:to="{ path: route.path }"
|
||||||
|
>{{ route.meta.title }}</el-breadcrumb-item
|
||||||
|
>
|
||||||
|
</el-breadcrumb>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
import { watch, reactive, defineProps } from 'vue'
|
||||||
|
const route = useRoute()
|
||||||
|
const infoList = reactive([])
|
||||||
|
const props = defineProps({
|
||||||
|
separator: {
|
||||||
|
type: String,
|
||||||
|
default: '/'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
watch(
|
||||||
|
route,
|
||||||
|
() => {
|
||||||
|
infoList.splice(0, infoList.length)
|
||||||
|
route.matched.forEach((value) => {
|
||||||
|
if (value.meta && value.meta.title) {
|
||||||
|
infoList.push(value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
</script>
|
||||||
|
<style lang="" scoped></style>
|
||||||
254
src/components/common.js
Normal file
254
src/components/common.js
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
|
// 时间戳转日期
|
||||||
|
export function formatDate(now) {
|
||||||
|
var date = new Date(now)
|
||||||
|
var Y = date.getFullYear() + '-'
|
||||||
|
var M = (date.getMonth()+1 < 10 ? '0'+(date.getMonth() + 1) : date.getMonth() + 1) + '-'
|
||||||
|
var D = (date.getDate() < 10 ? '0'+(date.getDate()) : date.getDate()) + ' '
|
||||||
|
var h = (date.getHours() < 10 ? "0" + date.getHours() : date.getHours()) + ':'
|
||||||
|
var m = (date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes()) + ':'
|
||||||
|
var s = date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds()
|
||||||
|
return Y + M + D + h + m + s
|
||||||
|
}
|
||||||
|
|
||||||
|
export function dateTimeStr(str = "y-m-d h:i:s", day){
|
||||||
|
var date = new Date(day),
|
||||||
|
year = date.getFullYear(), //年
|
||||||
|
month = date.getMonth() + 1, //月
|
||||||
|
day = date.getDate(), //日
|
||||||
|
hour = date.getHours(), //时
|
||||||
|
minute = date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes(), //分
|
||||||
|
second = date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds(); //秒
|
||||||
|
month >= 1 && month <= 9 ? (month = "0" + month) : "";
|
||||||
|
day >= 0 && day <= 9 ? (day = "0" + day) : "";
|
||||||
|
hour >= 0 && hour <= 9 ? (hour = "0" + hour) : "";
|
||||||
|
|
||||||
|
if(str.indexOf('y') != -1){
|
||||||
|
str = str.replace('y', year)
|
||||||
|
}
|
||||||
|
if(str.indexOf('m') != -1){
|
||||||
|
str = str.replace('m', month)
|
||||||
|
}
|
||||||
|
if(str.indexOf('d') != -1){
|
||||||
|
str = str.replace('d', day)
|
||||||
|
}
|
||||||
|
if(str.indexOf('h') != -1){
|
||||||
|
str = str.replace('h', hour)
|
||||||
|
}
|
||||||
|
if(str.indexOf('i') != -1){
|
||||||
|
str = str.replace('i', minute)
|
||||||
|
}
|
||||||
|
if(str.indexOf('s') != -1){
|
||||||
|
str = str.replace('s', second)
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 比较两个时间大小
|
||||||
|
export function compareDate(date1, date2) {
|
||||||
|
let str1 = new Date(date1.replace(/-/g, "/")).getTime()
|
||||||
|
let str2 = new Date(date2.replace(/-/g, "/")).getTime()
|
||||||
|
return str2 - str1 >= 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取当前日期前几天的日期
|
||||||
|
export function getOldDay(day) {
|
||||||
|
var dd = new Date();
|
||||||
|
dd.setDate(dd.getDate()-day); //获取AddDayCount天后的日期
|
||||||
|
return formatDate(new Date(dd))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 循环数组的笛卡尔积
|
||||||
|
export function cartesianProductOf () {
|
||||||
|
return Array.prototype.reduce.call(arguments, (a, b) => {
|
||||||
|
const ret = []
|
||||||
|
a.forEach(function (a) {
|
||||||
|
b.forEach(function (b) {
|
||||||
|
ret.push(a.concat([b]))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return ret
|
||||||
|
}, [[]])
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 复制
|
||||||
|
* @param {*} id DOM ID
|
||||||
|
*/
|
||||||
|
export function copyDomText (id) {
|
||||||
|
const node = document.getElementById(id)
|
||||||
|
if (node) {
|
||||||
|
let createRange = document.createRange()
|
||||||
|
createRange.selectNodeContents(document.getElementById(id))
|
||||||
|
const selection = document.getSelection()
|
||||||
|
selection.removeAllRanges()
|
||||||
|
selection.addRange(createRange)
|
||||||
|
document.execCommand('Copy')
|
||||||
|
selection.removeAllRanges()
|
||||||
|
alert('已复制')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function arrayOperations(arr, newIndex, oldIndex) {
|
||||||
|
arr[newIndex] = arr.splice(oldIndex, 1, arr[newIndex])[0]
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
|
||||||
|
export function variableType(val) {
|
||||||
|
const valEnum = {
|
||||||
|
'[object String]': 'String',
|
||||||
|
'[object Number]': 'Number',
|
||||||
|
'[object Array]': 'Array',
|
||||||
|
'[object Object]': 'Object',
|
||||||
|
'[object Null]': 'Null',
|
||||||
|
'[object Undefined]': 'Undefined',
|
||||||
|
'[object Boolean]': 'Boolean',
|
||||||
|
'[object Function]': 'Function',
|
||||||
|
}
|
||||||
|
return valEnum[Object.prototype.toString.call(val)]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function clearForm(data) {
|
||||||
|
console.log('data========', data)
|
||||||
|
if (variableType(data) !== 'Object') {
|
||||||
|
console.log('data必须要是对象')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Object.keys(data).forEach((item) => {
|
||||||
|
if (variableType(data[item]) === 'String') {
|
||||||
|
data[item] = ''
|
||||||
|
}
|
||||||
|
if (variableType(data[item]) === 'Null') {
|
||||||
|
data[item] = null
|
||||||
|
}
|
||||||
|
if (variableType(data[item]) === 'Boolean') {
|
||||||
|
data[item] = false
|
||||||
|
}
|
||||||
|
if (variableType(data[item]) === 'Undefined') {
|
||||||
|
data[item] = undefined
|
||||||
|
}
|
||||||
|
if (variableType(data[item]) === 'Number') {
|
||||||
|
data[item] = 0
|
||||||
|
}
|
||||||
|
if (variableType(data[item]) === 'Object') {
|
||||||
|
console.log('是对象')
|
||||||
|
clearForm(data[item])
|
||||||
|
}
|
||||||
|
if (variableType(data[item]) === 'Array') {
|
||||||
|
data[item] = []
|
||||||
|
}
|
||||||
|
return item
|
||||||
|
})
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 时间段转时间戳
|
||||||
|
* @param {*} t
|
||||||
|
* @return {*}
|
||||||
|
*/
|
||||||
|
export function getTargetTime(t) {
|
||||||
|
var d = t.split(' ')[0],
|
||||||
|
h = t.split(' ')[1],
|
||||||
|
date = new Date()
|
||||||
|
date.setYear(d.split('-')[0])
|
||||||
|
date.setMonth(d.split('-')[1] - 1)
|
||||||
|
date.setDate(d.split('-')[2])
|
||||||
|
date.setHours(h.split(':')[0])
|
||||||
|
date.setMinutes(h.split(':')[1])
|
||||||
|
date.setSeconds(h.split(':')[2])
|
||||||
|
return date.getTime()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 时间戳转时间段
|
||||||
|
export function formatDate1(item) {
|
||||||
|
var date = new Date(item)
|
||||||
|
var YY = date.getFullYear() + '-'
|
||||||
|
var MM = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'
|
||||||
|
var DD = date.getDate() < 10 ? '0' + date.getDate() : date.getDate()
|
||||||
|
var hh = (date.getHours() < 10 ? '0' + date.getHours() : date.getHours()) + ':'
|
||||||
|
var mm = (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()) + ':'
|
||||||
|
var ss = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()
|
||||||
|
return YY + MM + DD + ' ' + hh + mm + ss
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 复制内容
|
||||||
|
export function copyContent(value) {
|
||||||
|
var input = document.createElement('input')
|
||||||
|
// 把文字放进input中,供复制
|
||||||
|
input.value = value
|
||||||
|
document.body.appendChild(input)
|
||||||
|
// 选中创建的input
|
||||||
|
input.select()
|
||||||
|
var copy_result = document.execCommand('copy')
|
||||||
|
if (copy_result) {
|
||||||
|
ElMessage({ type: 'success', message: '复制成功' })
|
||||||
|
} else {
|
||||||
|
ElMessage({ type: 'error', message: '复制失败' })
|
||||||
|
}
|
||||||
|
document.body.removeChild(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断标题是否有特殊字符(表情)
|
||||||
|
export function isEmojiCharacter(substring){
|
||||||
|
for (var i = 0; i < substring.length; i ++){
|
||||||
|
var hs = substring.charCodeAt(i);
|
||||||
|
if (0xd800 <= hs && hs <= 0xdbff){
|
||||||
|
if (substring.length> 1){
|
||||||
|
var ls = substring.charCodeAt(i + 1);
|
||||||
|
var uc =((hs - 0xd800)* 0x400)+(ls - 0xdc00)+ 0x10000;
|
||||||
|
if (0x1d000 <= uc && uc <= 0x1f77f){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (substring.length> 1){
|
||||||
|
var ls = substring.charCodeAt(i + 1);
|
||||||
|
if (ls == 0x20e3){
|
||||||
|
return true ;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (0x2100 <= hs && hs <= 0x27ff){
|
||||||
|
return true ;
|
||||||
|
} else if (0x2B05 <= hs && hs <= 0x2b07){
|
||||||
|
return true ;
|
||||||
|
} else if (0x2934 <= hs && hs <= 0x2935){
|
||||||
|
return true ;
|
||||||
|
} else if (0x3297 <= hs && hs <= 0x3299){
|
||||||
|
return true ;
|
||||||
|
} else if (hs == 0xa9 || hs == 0xae || hs == 0x303d || hs == 0x3030
|
||||||
|
|| hs == 0x2b55 || hs == 0x2b1c || hs == 0x2b1b
|
||||||
|
|| hs == 0x2b50){
|
||||||
|
return true ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function dateFormatTxt(time) {
|
||||||
|
const delta = (new Date().getTime() - new Date(time).getTime()) / 1000
|
||||||
|
if (delta / (60 * 60 * 24 * 365) > 1)
|
||||||
|
return `${parseInt(delta / (60 * 60 * 24 * 365))}年前`
|
||||||
|
if (delta / (60 * 60 * 24 * 30) > 1)
|
||||||
|
return `${parseInt(delta / (60 * 60 * 24 * 30))}个月前`
|
||||||
|
if (delta / (60 * 60 * 24 * 7) > 1)
|
||||||
|
return `${parseInt(delta / (60 * 60 * 24 * 7))}周前`
|
||||||
|
if (delta / (60 * 60 * 24) > 1)
|
||||||
|
return `${parseInt(delta / (60 * 60 * 24))}天前`
|
||||||
|
if (delta / (60 * 60) > 1) return `${parseInt(delta / (60 * 60))}小时前`
|
||||||
|
if (delta / 60 > 1) return `${parseInt(delta / 60)}分钟前`
|
||||||
|
return '刚刚'
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseErrors(errors) {
|
||||||
|
if(errors && Object.keys(errors).length !== 0) {
|
||||||
|
for (let key in errors) {
|
||||||
|
if (errors.hasOwnProperty(key)) {
|
||||||
|
errors[key].forEach((it) => {
|
||||||
|
ElMessage({ message: it, type: 'info', customClass: 'vanMsg' })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/main.js
Normal file
18
src/main.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
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 zhCn from 'element-plus/es/locale/lang/zh-cn'
|
||||||
|
import '@/assets/icon/iconfont.css'
|
||||||
|
import '@/assets/main.scss'
|
||||||
|
|
||||||
|
createApp(App)
|
||||||
|
.use(store)
|
||||||
|
.use(router)
|
||||||
|
.use(ElementPlus, { locale: zhCn, zIndex: 3000, size: 'default' })
|
||||||
|
.mount('#app')
|
||||||
|
|
||||||
|
document.title = 'ERP-管理系统';
|
||||||
|
document.execCommand("BackgroundImageCache", false, true);
|
||||||
89
src/router/index.js
Normal file
89
src/router/index.js
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||||
|
import Layout from 'views/layout/index.vue'
|
||||||
|
import { constantRouterComponents } from './white'
|
||||||
|
|
||||||
|
export const routes = [
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
redirect: '/login',
|
||||||
|
hidden: true,
|
||||||
|
component: () => import('views/login/index.vue'),
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/login',
|
||||||
|
name: 'login',
|
||||||
|
hidden: true,
|
||||||
|
component: () => import('views/login/index.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/404',
|
||||||
|
name: '404',
|
||||||
|
hidden: true,
|
||||||
|
component: () => import('views/login/404.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/layout',
|
||||||
|
name: 'Layout',
|
||||||
|
component: Layout,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '/home',
|
||||||
|
name: 'home',
|
||||||
|
title: '首页',
|
||||||
|
component: () => import('views/home/index.vue')
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
export function getMenusRoute() {
|
||||||
|
let list = []
|
||||||
|
let menus = JSON.parse(localStorage.getItem('menus'))
|
||||||
|
menus && menus.forEach((item) => {
|
||||||
|
if(item.path != '/home') {
|
||||||
|
let obj = {
|
||||||
|
path: item.path,
|
||||||
|
name: item.name,
|
||||||
|
full_path: item.full_path,
|
||||||
|
component: (constantRouterComponents[item.name]) || (() => import(`${item.full_path}`))
|
||||||
|
}
|
||||||
|
list.push(obj)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHashHistory(),
|
||||||
|
routes
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
let registerRouteFresh = true
|
||||||
|
router.beforeEach((to, from, next) => {
|
||||||
|
if (to.path !== '/login' && to.path !== '/chooseUser' && to.path !== '/homeLogin' && to.path !== '/backstage') {
|
||||||
|
if (localStorage.getItem('token')) {
|
||||||
|
let list = getMenusRoute()
|
||||||
|
if (registerRouteFresh && list.length) {
|
||||||
|
list.forEach((itemRouter) => {
|
||||||
|
router.addRoute('Layout', itemRouter)
|
||||||
|
})
|
||||||
|
router.addRoute({
|
||||||
|
path: '/:pathMatch(.*)',
|
||||||
|
redirect: '/404'
|
||||||
|
})
|
||||||
|
next(to)
|
||||||
|
registerRouteFresh = false
|
||||||
|
} else {
|
||||||
|
next()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
next('/login')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
next()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
||||||
96
src/router/white.js
Normal file
96
src/router/white.js
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
export const whiteList = [
|
||||||
|
{
|
||||||
|
path: '/commodity/list',
|
||||||
|
name: 'commodityList',
|
||||||
|
full_path: 'views/commodity/list.vue',
|
||||||
|
component: () => import('views/commodity/list')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/commodity/all',
|
||||||
|
name: 'allgoods',
|
||||||
|
full_path: 'views/commodity/all.vue',
|
||||||
|
component: () => import('views/commodity/all')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/commodity/sku',
|
||||||
|
name: 'commoditySku',
|
||||||
|
full_path: 'views/commodity/sku.vue',
|
||||||
|
component: () => import('views/commodity/sku')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/commodity/combination',
|
||||||
|
name: 'combination',
|
||||||
|
full_path: 'views/commodity/combination.vue',
|
||||||
|
component: () => import('views/commodity/combination')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/commodity/brand',
|
||||||
|
name: 'commodityBrand',
|
||||||
|
full_path: 'views/commodity/brand.vue',
|
||||||
|
component: () => import('views/commodity/brand')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/purchase/index',
|
||||||
|
name: 'purchase',
|
||||||
|
full_path: 'views/purchase/index.vue',
|
||||||
|
component: () => import('views/purchase/index')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/warehouse/index',
|
||||||
|
name: 'warehouse',
|
||||||
|
full_path: 'views/warehouse/index.vue',
|
||||||
|
component: () => import('views/warehouse/index')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/warehouse/io',
|
||||||
|
name: 'warehouseIO',
|
||||||
|
full_path: 'views/warehouse/io.vue',
|
||||||
|
component: () => import('views/warehouse/io')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/supplier/index',
|
||||||
|
name: 'supplier',
|
||||||
|
full_path: 'views/supplier/index.vue',
|
||||||
|
component: () => import('views/supplier/index')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/statistics/index',
|
||||||
|
name: 'statistics',
|
||||||
|
full_path: 'views/statistics/index.vue',
|
||||||
|
component: () => import('views/statistics/index')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/permission/role',
|
||||||
|
name: 'role',
|
||||||
|
full_path: 'views/permission/role.vue',
|
||||||
|
component: () => import('views/permission/role')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/permission/menus',
|
||||||
|
name: 'menus',
|
||||||
|
full_path: 'views/permission/menus.vue',
|
||||||
|
component: () => import('views/permission/menus')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/shops/index',
|
||||||
|
name: 'shops',
|
||||||
|
full_path: 'views/shops/index.vue',
|
||||||
|
component: () => import('views/shops/index')
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
export const constantRouterComponents = {
|
||||||
|
'commodityList': () => import('views/commodity/list'),
|
||||||
|
'allgoods': () => import('views/commodity/all'),
|
||||||
|
'commoditySku': () => import('views/commodity/sku'),
|
||||||
|
'combination': () => import('views/commodity/combination'),
|
||||||
|
'commodityBrand': () => import('views/commodity/brand'),
|
||||||
|
'purchase': () => import('views/purchase/index'),
|
||||||
|
'warehouse': () => import('views/warehouse/index'),
|
||||||
|
'warehouseIO': () => import('views/warehouse/io'),
|
||||||
|
'supplier': () => import('views/supplier/index'),
|
||||||
|
'statistics': () => import('views/statistics/index'),
|
||||||
|
'role': () => import('views/permission/role'),
|
||||||
|
'menus': () => import('views/permission/menus'),
|
||||||
|
'shops': () => import('views/shops/index')
|
||||||
|
}
|
||||||
82
src/store/index.js
Normal file
82
src/store/index.js
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import { createStore } from 'vuex'
|
||||||
|
|
||||||
|
export default createStore({
|
||||||
|
state: {
|
||||||
|
msgNum: 0,
|
||||||
|
orderNum: 0,
|
||||||
|
noticeNum: 0,
|
||||||
|
workNum: 0,
|
||||||
|
tousuNum: 0,
|
||||||
|
annNum: 0,
|
||||||
|
orderType: '',
|
||||||
|
orderId: '',
|
||||||
|
workId: '',
|
||||||
|
msgDialog: false
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
SET_MSGNUM: (state, num) => {
|
||||||
|
state.msgNum = num
|
||||||
|
},
|
||||||
|
SET_ORDERNUM: (state, num) => {
|
||||||
|
state.orderNum = num
|
||||||
|
},
|
||||||
|
SET_NOTICENUM: (state, num) => {
|
||||||
|
state.noticeNum = num
|
||||||
|
},
|
||||||
|
SET_WORKNUM: (state, num) => {
|
||||||
|
state.workNum = num
|
||||||
|
},
|
||||||
|
SET_TOUSUNUM: (state, num) => {
|
||||||
|
state.tousuNum = num
|
||||||
|
},
|
||||||
|
SET_ORDERTYPE: (state, val) => {
|
||||||
|
state.orderType = val
|
||||||
|
},
|
||||||
|
SET_ORDERID: (state, val) => {
|
||||||
|
state.orderId = val
|
||||||
|
},
|
||||||
|
SET_WORKID: (state, val) => {
|
||||||
|
state.workId = val
|
||||||
|
},
|
||||||
|
SET_MSGDIALOG: (state, flag) => {
|
||||||
|
state.msgDialog = flag
|
||||||
|
},
|
||||||
|
SET_ANNNUM: (state, num) => {
|
||||||
|
state.annNum = num
|
||||||
|
},
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
setMsgNum({ commit }, num) {
|
||||||
|
commit('SET_MSGNUM', num)
|
||||||
|
},
|
||||||
|
setOrderNum({ commit }, num) {
|
||||||
|
commit('SET_ORDERNUM', num)
|
||||||
|
},
|
||||||
|
setNoticeNum({ commit }, num) {
|
||||||
|
commit('SET_NOTICENUM', num)
|
||||||
|
},
|
||||||
|
setWorkNum({ commit }, num) {
|
||||||
|
commit('SET_WORKNUM', num)
|
||||||
|
},
|
||||||
|
setTousuNum({ commit }, num) {
|
||||||
|
commit('SET_TOUSUNUM', num)
|
||||||
|
},
|
||||||
|
setOrderType({ commit }, val) {
|
||||||
|
commit('SET_ORDERTYPE', val)
|
||||||
|
},
|
||||||
|
setOrderId({ commit }, val) {
|
||||||
|
commit('SET_ORDERID', val)
|
||||||
|
},
|
||||||
|
setWorkId({ commit }, val) {
|
||||||
|
commit('SET_WORKID', val)
|
||||||
|
},
|
||||||
|
setMsgDialog({ commit }, flag) {
|
||||||
|
commit('SET_MSGDIALOG', flag)
|
||||||
|
},
|
||||||
|
setAnnNum({ commit }, num) {
|
||||||
|
commit('SET_ANNNUM', num)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modules: {
|
||||||
|
}
|
||||||
|
})
|
||||||
220
src/views/commodity/all.vue
Normal file
220
src/views/commodity/all.vue
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
<template>
|
||||||
|
<div class="pageBox">
|
||||||
|
<div class="searchBox">
|
||||||
|
<div class="row row1">
|
||||||
|
<span class="span">商品名称:</span>
|
||||||
|
<div class="right"><el-input v-model="filter.keyword" class="wid100" clearable></el-input></div>
|
||||||
|
</div>
|
||||||
|
<div class="row row1">
|
||||||
|
<span class="span">商品编码:</span>
|
||||||
|
<div class="right"><el-input v-model="filter.goods_code" class="wid100" clearable></el-input></div>
|
||||||
|
</div>
|
||||||
|
<div class="row row1">
|
||||||
|
<span class="span">规格编码:</span>
|
||||||
|
<div class="right"><el-input v-model="filter.sku_code" class="wid100" clearable></el-input></div>
|
||||||
|
</div>
|
||||||
|
<div class="row row1">
|
||||||
|
<span class="span">是否单品:</span>
|
||||||
|
<div class="right">
|
||||||
|
<el-select v-model="filter.single" placeholder="请选择" clearable class="wid100">
|
||||||
|
<el-option label="组合商品" :value="0" />
|
||||||
|
<el-option label="单品" :value="1" />
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row row1">
|
||||||
|
<span class="span">是否售卖:</span>
|
||||||
|
<div class="right">
|
||||||
|
<el-select v-model="filter.on_sale" placeholder="请选择" clearable class="wid100">
|
||||||
|
<el-option label="是" :value="1" />
|
||||||
|
<el-option label="否" :value="0" />
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<el-button type="primary" @click="handleSearch"><el-icon><Search /></el-icon> 筛选</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-card shadow="never">
|
||||||
|
<el-table :data="goodsList" style="width: 100%" border v-loading="loading">
|
||||||
|
<el-table-column prop="id" label="ID" align="center" width="80" />
|
||||||
|
<el-table-column prop="type" label="商品信息">
|
||||||
|
<template #default="scope">
|
||||||
|
<div>{{ scope.row.goods_name }}</div>
|
||||||
|
<span style="color: #666;">{{ scope.row.goods_code }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<!-- <el-table-column prop="goods_stock" label="商品库存" align="center" /> -->
|
||||||
|
|
||||||
|
<el-table-column prop="type" label="规格信息">
|
||||||
|
<template #default="scope">
|
||||||
|
<div>{{ scope.row.sku_name }}</div>
|
||||||
|
<span style="color: #666;">{{ scope.row.sku_code }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="sku_number" label="规格数量" align="center" />
|
||||||
|
<el-table-column prop="sku_stock" label="规格库存" align="center" />
|
||||||
|
|
||||||
|
<el-table-column prop="type" label="是否单品" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag type="primary" v-if="scope.row.single === 0">组合商品</el-tag>
|
||||||
|
<el-tag type="danger" v-else-if="scope.row.single === 1">单品</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="是否售卖" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-switch
|
||||||
|
v-model="scope.row.on_sale"
|
||||||
|
:active-value="1"
|
||||||
|
:inactive-value="0"
|
||||||
|
active-text="是"
|
||||||
|
inactive-text="否"
|
||||||
|
inline-prompt
|
||||||
|
:loading="scope.row.loading"
|
||||||
|
:before-change="()=>beforeChange($event, scope.row)" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<!-- <el-table-column prop="created_at" label="创建时间" align="center" /> -->
|
||||||
|
</el-table>
|
||||||
|
<div class="page-pagination">
|
||||||
|
<el-pagination
|
||||||
|
:current-page="page"
|
||||||
|
background
|
||||||
|
layout="prev, pager, next, sizes, total"
|
||||||
|
:total="total"
|
||||||
|
:page-sizes="[10, 50, 100, 500, 1000]"
|
||||||
|
:page-size="pageSize"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange"></el-pagination>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { onMounted, reactive, toRefs } from "vue"
|
||||||
|
import { get, post } from "@/api/request"
|
||||||
|
import { Search } from '@element-plus/icons'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Search
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const data = reactive({
|
||||||
|
filter: {},
|
||||||
|
goodsList: [],
|
||||||
|
page: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0,
|
||||||
|
loading: false
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleSearch() {
|
||||||
|
data.page = 1
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchData = () => {
|
||||||
|
data.loading = true
|
||||||
|
let params = {
|
||||||
|
...data.filter,
|
||||||
|
page: data.page,
|
||||||
|
pageSize: data.pageSize
|
||||||
|
}
|
||||||
|
get(`/api/mxErpGoods`, params).then((res) => {
|
||||||
|
data.goodsList = res.data
|
||||||
|
data.total = res.meta.total
|
||||||
|
data.loading = false
|
||||||
|
}).catch(() => {
|
||||||
|
data.loading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCurrentChange(e) {
|
||||||
|
data.page = e
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSizeChange(e) {
|
||||||
|
data.page = 1
|
||||||
|
data.pageSize = e
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
const beforeChange = (e, row) => {
|
||||||
|
let on_sale = row.on_sale
|
||||||
|
row.loading = true
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
ElMessageBox.confirm('确定修改该状态吗?', '警告', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
post(`/api/mxErpGoods/${row.id}`, {on_sale: on_sale == 1 ? 0 : 1}, 'PUT').then(() => {
|
||||||
|
ElMessage({ type: 'success', message: '修改成功' })
|
||||||
|
row.loading = false
|
||||||
|
return resolve(true)
|
||||||
|
}).catch(() => {
|
||||||
|
row.loading = false
|
||||||
|
return reject(new Error('Error'))
|
||||||
|
})
|
||||||
|
}).catch(() => {
|
||||||
|
row.loading = false
|
||||||
|
return reject(new Error('Error'))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchData()
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toRefs(data),
|
||||||
|
handleSearch,
|
||||||
|
handleCurrentChange,
|
||||||
|
handleSizeChange,
|
||||||
|
fetchData,
|
||||||
|
beforeChange
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.searchBox{
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 15px 0 0 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
.row{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: auto;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
margin-right: 15px;
|
||||||
|
&.row1{
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
.span{
|
||||||
|
display: block;
|
||||||
|
width: 80px;
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: right;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.right{
|
||||||
|
width: calc(100% - 80px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
275
src/views/commodity/brand.vue
Normal file
275
src/views/commodity/brand.vue
Normal file
@ -0,0 +1,275 @@
|
|||||||
|
<template>
|
||||||
|
<div class="pageBox">
|
||||||
|
<!-- <div class="searchBox">
|
||||||
|
<div class="row">
|
||||||
|
<span class="span">商品名称:</span>
|
||||||
|
<div class="right"><el-input v-model="goods_name" class="wid100" clearable></el-input></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<el-button type="primary" @click="handleSearch"><el-icon><Search /></el-icon> 筛选</el-button>
|
||||||
|
</div>
|
||||||
|
</div> -->
|
||||||
|
|
||||||
|
<el-card shadow="never">
|
||||||
|
<div class="opaBox">
|
||||||
|
<el-button type="primary" @click="handleAdd"><el-icon><Plus /></el-icon> 新增</el-button>
|
||||||
|
<!-- <el-button type="warning" @click="handleExport"><span class="iconfont icon-daochu"></span> 导出</el-button> -->
|
||||||
|
</div>
|
||||||
|
<el-table :data="goodsList" style="width: 100%" border v-loading="loading">
|
||||||
|
<el-table-column prop="id" label="ID" width="80" align="center" />
|
||||||
|
<el-table-column prop="name" label="品牌名称" align="center" />
|
||||||
|
<el-table-column prop="type" label="品牌类型" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<span>{{ brandType[scope.row.type] }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="品牌状态" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<span>{{ scope.row.status ? '启用' : '不启用' }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="admin_user.username" label="添加人" align="center" />
|
||||||
|
<el-table-column prop="note" label="备注" align="center" />
|
||||||
|
<el-table-column prop="sort" label="排序" align="center" />
|
||||||
|
<el-table-column label="操作" align="center" width="150">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button type="primary" circle @click="handleEdit(scope.row)"><el-icon><Edit /></el-icon></el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div class="page-pagination">
|
||||||
|
<el-pagination
|
||||||
|
background
|
||||||
|
:current-page="page"
|
||||||
|
layout="prev, pager, next, sizes, total"
|
||||||
|
:total="total"
|
||||||
|
:page-sizes="[10, 50, 100]"
|
||||||
|
:page-size="pageSize"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange"></el-pagination>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-dialog v-model="showDialog" width="900px" :title="opaType == 'add' ? '新增' : '编辑' ">
|
||||||
|
<el-form label-position="right" label-width="110px">
|
||||||
|
<el-form-item label="品牌名称:">
|
||||||
|
<el-input v-model="itemInfo.name" clearable></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="品牌类型:">
|
||||||
|
<el-radio-group v-model="itemInfo.type">
|
||||||
|
<el-radio label="self">自有</el-radio>
|
||||||
|
<el-radio label="custom">定制</el-radio>
|
||||||
|
<el-radio label="default">默认</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="品牌状态:">
|
||||||
|
<el-radio-group v-model="itemInfo.status">
|
||||||
|
<el-radio :label="1">启用</el-radio>
|
||||||
|
<el-radio :label="0">不启用</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="备注:">
|
||||||
|
<el-input v-model="itemInfo.note" type="textarea" :rows="4"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="排序:">
|
||||||
|
<el-input-number v-model="itemInfo.sort" :min="0" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="showDialog = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="commitOpa()" :loading="opa_loading">确定</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { onMounted, reactive, toRefs } from "vue"
|
||||||
|
import { get, post } from "@/api/request"
|
||||||
|
import { Search, Plus, Edit, ZoomIn, Delete } from '@element-plus/icons'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { parseErrors } from 'components/common'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Search, Plus, Edit, ZoomIn, Delete
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const data = reactive({
|
||||||
|
goodsList: [],
|
||||||
|
page: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0,
|
||||||
|
loading: false,
|
||||||
|
pickerTime: [],
|
||||||
|
opaType: '',
|
||||||
|
showDialog: false,
|
||||||
|
opa_loading: false,
|
||||||
|
brandType: {
|
||||||
|
'self': '自有',
|
||||||
|
'custom': '定制',
|
||||||
|
'default': '默认'
|
||||||
|
},
|
||||||
|
itemInfo: {}
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleSearch() {
|
||||||
|
data.page = 1
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchData = () => {
|
||||||
|
data.loading = true
|
||||||
|
let params = {
|
||||||
|
page: data.page,
|
||||||
|
pageSize: data.pageSize,
|
||||||
|
service_id: data.service_id,
|
||||||
|
start_date: data.pickerTime ? data.pickerTime[0] : '',
|
||||||
|
end_date: data.pickerTime ? data.pickerTime[1] : ''
|
||||||
|
}
|
||||||
|
get(`/api/brands`, params).then((res) => {
|
||||||
|
data.goodsList = res.data
|
||||||
|
data.total = res.meta.total
|
||||||
|
data.loading = false
|
||||||
|
}).catch(() => {
|
||||||
|
data.loading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCurrentChange(e) {
|
||||||
|
data.page = e
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSizeChange(e) {
|
||||||
|
data.page = 1
|
||||||
|
data.pageSize = e
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleAdd() {
|
||||||
|
data.opaType = 'add'
|
||||||
|
data.itemInfo = {
|
||||||
|
status: 1,
|
||||||
|
sort: 0
|
||||||
|
}
|
||||||
|
data.showDialog = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleEdit(item) {
|
||||||
|
data.opaType = 'edit'
|
||||||
|
data.itemInfo = JSON.parse(JSON.stringify(item))
|
||||||
|
data.showDialog = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function commitOpa() {
|
||||||
|
data.opa_loading = true
|
||||||
|
let params = {
|
||||||
|
...data.itemInfo
|
||||||
|
}
|
||||||
|
if(data.opaType == 'add') {
|
||||||
|
post(`/api/brands`, params).then(() => {
|
||||||
|
data.page = 1
|
||||||
|
fetchData()
|
||||||
|
ElMessage({ type: 'success', message: '新增成功' })
|
||||||
|
data.showDialog = false
|
||||||
|
data.opa_loading = false
|
||||||
|
}).catch((err) => {
|
||||||
|
data.opa_loading = false
|
||||||
|
parseErrors(err.response.data.errors)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
post(`/api/brands/${data.itemInfo.id}`, params, 'PUT').then(() => {
|
||||||
|
fetchData()
|
||||||
|
ElMessage({ type: 'success', message: '编辑成功' })
|
||||||
|
data.showDialog = false
|
||||||
|
data.opa_loading = false
|
||||||
|
}).catch((err) => {
|
||||||
|
data.opa_loading = false
|
||||||
|
parseErrors(err.response.data.errors)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchData()
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toRefs(data),
|
||||||
|
handleSearch,
|
||||||
|
handleCurrentChange,
|
||||||
|
handleSizeChange,
|
||||||
|
fetchData,
|
||||||
|
handleAdd,
|
||||||
|
handleEdit,
|
||||||
|
commitOpa,
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.searchBox{
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 15px 0 0 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
.row{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 20%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
&.row1{
|
||||||
|
width: 25%;
|
||||||
|
}
|
||||||
|
.span{
|
||||||
|
display: block;
|
||||||
|
width: 80px;
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: right;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.right{
|
||||||
|
width: calc(100% - 100px);
|
||||||
|
}
|
||||||
|
.wid100{
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.opaBox{
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
.imgBox{
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
.el-image{
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.skuBox{
|
||||||
|
border: 1px solid #e5e5e5;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 15px 0;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
background-color: #f3f3f3;
|
||||||
|
.tit{
|
||||||
|
padding-left: 40px;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 15px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
339
src/views/commodity/combination.vue
Normal file
339
src/views/commodity/combination.vue
Normal file
@ -0,0 +1,339 @@
|
|||||||
|
<template>
|
||||||
|
<div class="pageBox">
|
||||||
|
<div class="searchBox">
|
||||||
|
<div class="row">
|
||||||
|
<span class="span">商品名称:</span>
|
||||||
|
<div class="right"><el-input v-model="filter.title" class="wid100" clearable></el-input></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<span class="span">组合编码:</span>
|
||||||
|
<div class="right"><el-input v-model="filter.combination_goods_code" class="wid100" clearable></el-input></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<span class="span">状态:</span>
|
||||||
|
<div class="right">
|
||||||
|
<el-select v-model="filter.status" placeholder="请选择" clearable class="wid100">
|
||||||
|
<el-option label="启用" :value="1" />
|
||||||
|
<el-option label="不启用" :value="0" />
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<span class="span"></span>
|
||||||
|
<div class="right"><el-button type="primary" @click="handleSearch"><el-icon><Search /></el-icon> 筛选</el-button></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-card shadow="never">
|
||||||
|
<div class="opaBox">
|
||||||
|
<el-button type="primary" @click="handleAdd"><el-icon><Plus /></el-icon> 新增</el-button>
|
||||||
|
<!-- <el-button type="warning" @click="handleExport"><span class="iconfont icon-daochu"></span> 导出</el-button> -->
|
||||||
|
</div>
|
||||||
|
<el-table :data="goodsList" style="width: 100%" border v-loading="loading">
|
||||||
|
<el-table-column prop="id" label="ID" width="80" align="center" />
|
||||||
|
<el-table-column prop="title" label="组合商品名称" align="center" />
|
||||||
|
<el-table-column prop="combination_goods_code" label="组合编码" align="center" />
|
||||||
|
<el-table-column prop="actual_inventory" label="实际库存" align="center" />
|
||||||
|
<el-table-column prop="lock_in_stock" label="锁定库存" align="center" />
|
||||||
|
<el-table-column prop="available_inventory" label="可售库存" align="center" />
|
||||||
|
<el-table-column label="状态" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<span>{{ scope.row.status ? '启用' : '不启用' }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="admin_user.username" label="添加人" align="center" />
|
||||||
|
<el-table-column prop="created_at" label="添加时间" align="center" />
|
||||||
|
<el-table-column label="操作" align="center" width="150">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button type="primary" circle @click="handleEdit(scope.row)"><el-icon><Edit /></el-icon></el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div class="page-pagination">
|
||||||
|
<el-pagination
|
||||||
|
background
|
||||||
|
:current-page="page"
|
||||||
|
layout="prev, pager, next, sizes, total"
|
||||||
|
:total="total"
|
||||||
|
:page-sizes="[10, 50, 100]"
|
||||||
|
:page-size="pageSize"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange"></el-pagination>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-dialog v-model="showDialog" width="900px" :title="opaType == 'add' ? '新增' : '编辑' ">
|
||||||
|
<el-form label-position="right" label-width="110px">
|
||||||
|
<el-form-item label="组合商品名称:">
|
||||||
|
<el-input v-model="itemInfo.title" clearable></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="组合编码:">
|
||||||
|
<el-input v-model="itemInfo.combination_goods_code" clearable></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="锁定库存:">
|
||||||
|
<el-input v-model="itemInfo.lock_in_stock" clearable></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="状态:">
|
||||||
|
<el-radio-group v-model="itemInfo.status">
|
||||||
|
<el-radio :label="1">启用</el-radio>
|
||||||
|
<el-radio :label="0">不启用</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="备注:">
|
||||||
|
<el-input v-model="itemInfo.note" type="textarea" :rows="4"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="排序:">
|
||||||
|
<el-input-number v-model="itemInfo.sort" :min="0" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label=" ">
|
||||||
|
<b style="margin-top: 20px;">参与组合商品</b>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
|
||||||
|
<div v-for="(item, i) in goodsSkus" :key="i" class="skuBox">
|
||||||
|
<el-form label-width="110px" :inline="true">
|
||||||
|
<el-form-item label="规格:">
|
||||||
|
<el-select v-model="item.sku_id" placeholder="请选择" clearable filterable style="width: 160px;">
|
||||||
|
<el-option v-for="it in skusList" :key="it.id" :label="it.goods && it.goods.title + '(' + it.title + ')'" :value="it.id" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="数量:">
|
||||||
|
<el-input-number v-model="item.num" :min="0" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label=" ">
|
||||||
|
<el-button type="danger" @click="handleSkuDelete(i)" size="small"><el-icon><Delete /></el-icon> 删除</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
<el-form label-position="right" label-width="110px">
|
||||||
|
<el-form-item label="">
|
||||||
|
<el-button type="success" @click="toAddSku()">新增</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="showDialog = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="commitOpa()" :loading="opa_loading">确定</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 预览图片 -->
|
||||||
|
<el-dialog v-model="picVisible" center width="800px">
|
||||||
|
<img :src="dialogImageUrl" style="max-width: 700px; margin: 0 auto;display: block;" />
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { onMounted, reactive, toRefs } from "vue"
|
||||||
|
import { get, post } from "@/api/request"
|
||||||
|
import { Search, Plus, Edit, ZoomIn, Delete } from '@element-plus/icons'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import { parseErrors } from 'components/common'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Search, Plus, Edit, ZoomIn, Delete
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const data = reactive({
|
||||||
|
filter: {},
|
||||||
|
goodsList: [],
|
||||||
|
page: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0,
|
||||||
|
loading: false,
|
||||||
|
opaType: '',
|
||||||
|
showDialog: false,
|
||||||
|
opa_loading: false,
|
||||||
|
itemInfo: {},
|
||||||
|
goodsSkus: [],
|
||||||
|
skusList: []
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleSearch() {
|
||||||
|
data.page = 1
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchData = () => {
|
||||||
|
data.loading = true
|
||||||
|
let params = {
|
||||||
|
page: data.page,
|
||||||
|
pageSize: data.pageSize,
|
||||||
|
...data.filter
|
||||||
|
}
|
||||||
|
get(`/api/combination-goods-skus`, params).then((res) => {
|
||||||
|
data.goodsList = res.data
|
||||||
|
data.total = res.meta.total
|
||||||
|
data.loading = false
|
||||||
|
}).catch(() => {
|
||||||
|
data.loading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCurrentChange(e) {
|
||||||
|
data.page = e
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSizeChange(e) {
|
||||||
|
data.page = 1
|
||||||
|
data.pageSize = e
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleAdd() {
|
||||||
|
data.opaType = 'add'
|
||||||
|
data.fileList = []
|
||||||
|
data.goodsSkus = []
|
||||||
|
data.itemInfo = {
|
||||||
|
sort: 0,
|
||||||
|
status: 1
|
||||||
|
}
|
||||||
|
data.showDialog = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleEdit(item) {
|
||||||
|
data.opaType = 'edit'
|
||||||
|
get(`/api/combination-goods-skus/${item.id}`).then((res) => {
|
||||||
|
data.itemInfo = res.data
|
||||||
|
data.goodsSkus = res.data.combination_goods_sku || []
|
||||||
|
data.showDialog = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function commitOpa() {
|
||||||
|
data.opa_loading = true
|
||||||
|
let params = {
|
||||||
|
...data.itemInfo
|
||||||
|
}
|
||||||
|
params.goodsSkus = data.goodsSkus
|
||||||
|
if(data.opaType == 'add') {
|
||||||
|
post(`/api/combination-goods-skus`, params).then(() => {
|
||||||
|
data.page = 1
|
||||||
|
fetchData()
|
||||||
|
ElMessage({ type: 'success', message: '新增成功' })
|
||||||
|
data.showDialog = false
|
||||||
|
data.opa_loading = false
|
||||||
|
}).catch((err) => {
|
||||||
|
data.opa_loading = false
|
||||||
|
parseErrors(err.response.data.errors)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
post(`/api/combination-goods-skus/${data.itemInfo.id}`, params, 'PUT').then(() => {
|
||||||
|
fetchData()
|
||||||
|
ElMessage({ type: 'success', message: '编辑成功' })
|
||||||
|
data.showDialog = false
|
||||||
|
data.opa_loading = false
|
||||||
|
}).catch((err) => {
|
||||||
|
data.opa_loading = false
|
||||||
|
parseErrors(err.response.data.errors)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSkuDelete(index) {
|
||||||
|
data.goodsSkus.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
function toAddSku() {
|
||||||
|
let sku = {
|
||||||
|
sku_id: '',
|
||||||
|
num: 1
|
||||||
|
}
|
||||||
|
data.goodsSkus.push(sku)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSkusList() {
|
||||||
|
get(`/api/all/goods-skus`).then((res) => {
|
||||||
|
data.skusList = res.data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchData()
|
||||||
|
getSkusList()
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toRefs(data),
|
||||||
|
handleSearch,
|
||||||
|
handleCurrentChange,
|
||||||
|
handleSizeChange,
|
||||||
|
fetchData,
|
||||||
|
handleAdd,
|
||||||
|
handleEdit,
|
||||||
|
commitOpa,
|
||||||
|
handleSkuDelete,
|
||||||
|
toAddSku,
|
||||||
|
getSkusList
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.searchBox{
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 15px 0 0 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
.row{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 20%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
&.row1{
|
||||||
|
width: 25%;
|
||||||
|
}
|
||||||
|
.span{
|
||||||
|
display: block;
|
||||||
|
width: 80px;
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: right;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.right{
|
||||||
|
width: calc(100% - 100px);
|
||||||
|
}
|
||||||
|
.wid100{
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.opaBox{
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
.imgBox{
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
.el-image{
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.skuBox{
|
||||||
|
border: 1px solid #e5e5e5;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 15px 0;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
background-color: #f3f3f3;
|
||||||
|
.tit{
|
||||||
|
padding-left: 40px;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 15px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
494
src/views/commodity/list.vue
Normal file
494
src/views/commodity/list.vue
Normal file
@ -0,0 +1,494 @@
|
|||||||
|
<template>
|
||||||
|
<div class="pageBox">
|
||||||
|
<div class="searchBox">
|
||||||
|
<div class="row">
|
||||||
|
<span class="span">商品名称:</span>
|
||||||
|
<div class="right"><el-input v-model="filter.title" class="wid100" clearable></el-input></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<span class="span">商品编码:</span>
|
||||||
|
<div class="right"><el-input v-model="filter.goods_code" class="wid100" clearable></el-input></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<span class="span">商品品牌:</span>
|
||||||
|
<div class="right">
|
||||||
|
<el-select v-model="filter.brand_id" placeholder="请选择" clearable class="wid100" filterable>
|
||||||
|
<el-option v-for="it in brandList" :key="it.id" :label="it.name" :value="it.id" />
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<span class="span">仓库:</span>
|
||||||
|
<div class="right">
|
||||||
|
<el-select v-model="filter.warehouse_id" placeholder="请选择" clearable class="wid100" filterable>
|
||||||
|
<el-option v-for="it in warehouseList" :key="it.id" :label="it.name" :value="it.id" />
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<span class="span">商品状态:</span>
|
||||||
|
<div class="right">
|
||||||
|
<el-select v-model="filter.status" placeholder="请选择" clearable class="wid100">
|
||||||
|
<el-option label="启用" :value="1" />
|
||||||
|
<el-option label="不启用" :value="0" />
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<span class="span"></span>
|
||||||
|
<div class="right"><el-button type="primary" @click="handleSearch"><el-icon><Search /></el-icon> 筛选</el-button></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-card shadow="never">
|
||||||
|
<div class="opaBox">
|
||||||
|
<el-button type="primary" @click="handleAdd"><el-icon><Plus /></el-icon> 新增</el-button>
|
||||||
|
<!-- <el-button type="warning" @click="handleExport"><span class="iconfont icon-daochu"></span> 导出</el-button> -->
|
||||||
|
</div>
|
||||||
|
<el-table :data="goodsList" style="width: 100%" border v-loading="loading">
|
||||||
|
<el-table-column prop="id" label="ID" width="80" align="center" />
|
||||||
|
<el-table-column prop="title" label="商品名称" align="center" />
|
||||||
|
<el-table-column prop="goods_code" label="商品编码" align="center" />
|
||||||
|
<el-table-column prop="brand.name" label="商品品牌" align="center" />
|
||||||
|
<el-table-column label="商品图片" align="center" min-width="150">
|
||||||
|
<template #default="scope">
|
||||||
|
<div class="imgBox">
|
||||||
|
<el-image :z-index="9999" v-for="(item, index) in scope.row.images" :key="index" :src="item" :hide-on-click-modal="true" :preview-src-list="[item]" fit="cover" :preview-teleported="true" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="warehouse.name" label="仓库" align="center" />
|
||||||
|
<el-table-column prop="introduce" label="说明" align="center" />
|
||||||
|
<el-table-column prop="admin_user.username" label="添加人" align="center" />
|
||||||
|
<el-table-column label="商品状态" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<span>{{ scope.row.status ? '启用' : '不启用' }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="sort" label="排序" align="center" />
|
||||||
|
<el-table-column prop="created_at" label="添加时间" align="center" />
|
||||||
|
<el-table-column label="操作" align="center" width="150">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button size="small" type="primary" circle @click="handleEdit(scope.row)"><el-icon><Edit /></el-icon></el-button>
|
||||||
|
<el-button size="small" circle @click="handleView(scope.row)"><el-icon><ZoomIn /></el-icon></el-button>
|
||||||
|
<el-button size="small" type="danger" circle @click="handleRemove(scope.row)"><el-icon><Delete /></el-icon></el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div class="page-pagination">
|
||||||
|
<el-pagination
|
||||||
|
:current-page="page"
|
||||||
|
background
|
||||||
|
layout="prev, pager, next, sizes, total"
|
||||||
|
:total="total"
|
||||||
|
:page-sizes="[10, 50, 100]"
|
||||||
|
:page-size="pageSize"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange"></el-pagination>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-dialog v-model="showDialog" width="900px" :title="opaType == 'view' ? '查看详情' : opaType == 'add' ? '新增' : '编辑' " @close="resetForm">
|
||||||
|
<el-form label-position="right" label-width="110px" :disabled="opaType == 'view'">
|
||||||
|
<el-form-item label="商品名称:">
|
||||||
|
<el-input v-model="itemInfo.title" clearable></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="商品编码:">
|
||||||
|
<el-input v-model="itemInfo.goods_code" clearable></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="商品品牌:">
|
||||||
|
<el-select v-model="itemInfo.brand_id" placeholder="请选择" clearable>
|
||||||
|
<el-option v-for="it in brandList" :key="it.id" :label="it.name" :value="it.id" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="商品图片:">
|
||||||
|
<el-upload
|
||||||
|
ref="uploadRef"
|
||||||
|
action="/api/upload/img"
|
||||||
|
:headers="headers"
|
||||||
|
:on-remove="handleRemoveImg"
|
||||||
|
:on-error="handleUploadError"
|
||||||
|
:on-preview="handlePreview"
|
||||||
|
:on-success="handleSuccess"
|
||||||
|
:file-list="fileList"
|
||||||
|
:show-file-list="true"
|
||||||
|
list-type="picture-card"
|
||||||
|
accept=".png,.jpg,.gif">
|
||||||
|
<el-icon><Plus /></el-icon>
|
||||||
|
</el-upload>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="仓库:">
|
||||||
|
<el-select v-model="itemInfo.warehouse_id" placeholder="请选择" clearable>
|
||||||
|
<el-option v-for="it in warehouseList" :key="it.id" :label="it.name" :value="it.id" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="说明:">
|
||||||
|
<el-input v-model="itemInfo.introduce" type="textarea" :rows="4"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="商品状态:">
|
||||||
|
<el-radio-group v-model="itemInfo.status">
|
||||||
|
<el-radio :label="1">启用</el-radio>
|
||||||
|
<el-radio :label="0">不启用</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="排序:">
|
||||||
|
<el-input-number v-model="itemInfo.sort" :min="0" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<div v-for="(item, i) in skusList" :key="i" class="skuBox">
|
||||||
|
<div class="tit">规格{{ i + 1 }}</div>
|
||||||
|
<el-form label-width="110px" :inline="true" :disabled="opaType == 'view'">
|
||||||
|
<el-form-item label="规格名称:">
|
||||||
|
<el-input placeholder="规格名称" v-model="item.title" clearable style="width: 250px;"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="规格编码:">
|
||||||
|
<el-input placeholder="规格编码" v-model="item.sku_code" clearable style="width: 250px;"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="仓库:">
|
||||||
|
<el-select v-model="item.warehouse_id" placeholder="请选择" clearable style="width: 250px;">
|
||||||
|
<el-option v-for="it in warehouseList" :key="it.id" :label="it.name" :value="it.id" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="商品状态:">
|
||||||
|
<el-radio-group v-model="item.status">
|
||||||
|
<el-radio :label="1">启用</el-radio>
|
||||||
|
<el-radio :label="0">不启用</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="赠品:">
|
||||||
|
<el-radio-group v-model="item.gift" style="width: 250px;">
|
||||||
|
<el-radio :label="0">不是</el-radio>
|
||||||
|
<el-radio :label="1">是</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="排序:">
|
||||||
|
<el-input-number v-model="item.sort" :min="0" />
|
||||||
|
<el-button type="danger" v-if="opaType != 'view'" @click="handleSkuDelete(i)" size="small"><el-icon><Delete /></el-icon> 删除</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
<el-form label-position="right" label-width="110px" v-if="opaType != 'view'">
|
||||||
|
<el-form-item label="">
|
||||||
|
<el-button type="success" @click="toAddSku()">增加规格</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer" v-if="opaType != 'view'">
|
||||||
|
<el-button @click="showDialog = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="commitOpa()" :loading="opa_loading">确定</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 预览图片 -->
|
||||||
|
<el-dialog v-model="picVisible" center width="800px">
|
||||||
|
<img :src="dialogImageUrl" style="max-width: 700px; margin: 0 auto;display: block;" />
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { onMounted, reactive, toRefs, ref, nextTick } from "vue"
|
||||||
|
import { get, post } from "@/api/request"
|
||||||
|
import { Search, Plus, Edit, ZoomIn, Delete } from '@element-plus/icons'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import { parseErrors } from 'components/common'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Search, Plus, Edit, ZoomIn, Delete
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const data = reactive({
|
||||||
|
filter: {},
|
||||||
|
goodsList: [],
|
||||||
|
page: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0,
|
||||||
|
loading: false,
|
||||||
|
opaType: '',
|
||||||
|
showDialog: false,
|
||||||
|
opa_loading: false,
|
||||||
|
headers: {
|
||||||
|
Authorization: localStorage.getItem('token'),
|
||||||
|
'Shop-Id': localStorage.getItem('shopId') || ''
|
||||||
|
},
|
||||||
|
picVisible: false,
|
||||||
|
dialogImageUrl: '',
|
||||||
|
fileList: [],
|
||||||
|
brandList: [],
|
||||||
|
warehouseList: [],
|
||||||
|
itemInfo: {},
|
||||||
|
skusList: []
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleSearch() {
|
||||||
|
data.page = 1
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchData = () => {
|
||||||
|
data.loading = true
|
||||||
|
let params = {
|
||||||
|
page: data.page,
|
||||||
|
pageSize: data.pageSize,
|
||||||
|
...data.filter
|
||||||
|
}
|
||||||
|
get(`/api/goods`, params).then((res) => {
|
||||||
|
data.goodsList = res.data
|
||||||
|
data.total = res.meta.total
|
||||||
|
data.loading = false
|
||||||
|
}).catch(() => {
|
||||||
|
data.loading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCurrentChange(e) {
|
||||||
|
data.page = e
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSizeChange(e) {
|
||||||
|
data.page = 1
|
||||||
|
data.pageSize = e
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleAdd() {
|
||||||
|
data.opaType = 'add'
|
||||||
|
data.fileList = []
|
||||||
|
data.skusList = []
|
||||||
|
data.itemInfo = {
|
||||||
|
status: 1,
|
||||||
|
sort: 0
|
||||||
|
}
|
||||||
|
data.showDialog = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleEdit(item) {
|
||||||
|
data.opaType = 'edit'
|
||||||
|
get(`/api/goods/${item.id}`).then((res) => {
|
||||||
|
data.itemInfo = res.data
|
||||||
|
data.fileList = []
|
||||||
|
if(res.data.images && res.data.images.length) {
|
||||||
|
res.data.images.forEach((it) => {
|
||||||
|
data.fileList.push({url: it})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
data.skusList = res.data.skus || []
|
||||||
|
data.showDialog = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleView(item) {
|
||||||
|
data.opaType = 'view'
|
||||||
|
get(`/api/goods/${item.id}`).then((res) => {
|
||||||
|
data.itemInfo = res.data
|
||||||
|
data.fileList = []
|
||||||
|
if(res.data.images && res.data.images.length) {
|
||||||
|
res.data.images.forEach((it) => {
|
||||||
|
data.fileList.push({url: it})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
data.skusList = res.data.skus || []
|
||||||
|
data.showDialog = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function commitOpa() {
|
||||||
|
data.opa_loading = true
|
||||||
|
let imgs = []
|
||||||
|
data.fileList.forEach((it) => {
|
||||||
|
imgs.push(it.url)
|
||||||
|
})
|
||||||
|
let params = {
|
||||||
|
...data.itemInfo
|
||||||
|
}
|
||||||
|
params.skus = data.skusList
|
||||||
|
params.images = imgs
|
||||||
|
if(data.opaType == 'add') {
|
||||||
|
post(`/api/goods`, params).then(() => {
|
||||||
|
data.page = 1
|
||||||
|
fetchData()
|
||||||
|
ElMessage({ type: 'success', message: '新增成功' })
|
||||||
|
data.showDialog = false
|
||||||
|
data.opa_loading = false
|
||||||
|
}).catch((err) => {
|
||||||
|
data.opa_loading = false
|
||||||
|
parseErrors(err.response.data.errors)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
post(`/api/goods/${data.itemInfo.id}`, params, 'PUT').then(() => {
|
||||||
|
fetchData()
|
||||||
|
ElMessage({ type: 'success', message: '编辑成功' })
|
||||||
|
data.showDialog = false
|
||||||
|
data.opa_loading = false
|
||||||
|
}).catch((err) => {
|
||||||
|
data.opa_loading = false
|
||||||
|
parseErrors(err.response.data.errors)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleRemoveImg(res, ress) {
|
||||||
|
data.fileList = ress
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleUploadError(err) {
|
||||||
|
if(JSON.parse(err.message) && JSON.parse(err.message).message) {
|
||||||
|
ElMessage({ type: 'error', message: JSON.parse(err.message).message })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePreview(File) {
|
||||||
|
data.dialogImageUrl = File.url
|
||||||
|
data.picVisible = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSuccess(res) {
|
||||||
|
data.fileList.push({url: res.data.link})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSkuDelete(index) {
|
||||||
|
data.skusList.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const uploadRef = ref(null)
|
||||||
|
|
||||||
|
const resetForm = () => {
|
||||||
|
data.fileList = []
|
||||||
|
nextTick(() => {
|
||||||
|
uploadRef.value.clearFiles()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function toAddSku() {
|
||||||
|
let sku = {
|
||||||
|
title: '',
|
||||||
|
sku_code: '',
|
||||||
|
warehouse_id: data.itemInfo.warehouse_id || '',
|
||||||
|
status: 1,
|
||||||
|
sort: 0,
|
||||||
|
gift: 0
|
||||||
|
}
|
||||||
|
data.skusList.push(sku)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleRemove(item) {
|
||||||
|
ElMessageBox.confirm('确定要删除当前数据吗?', '警告', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
post(`/api/goods/${item.id}`, {}, 'DELETE').then(() => {
|
||||||
|
getGroupList()
|
||||||
|
ElMessage({ type: 'success', message: '删除成功' })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBrandList() {
|
||||||
|
get(`/api/all/brands`).then((res) => {
|
||||||
|
data.brandList = res.data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function getWarehouseList() {
|
||||||
|
get(`/api/all/warehouses`).then((res) => {
|
||||||
|
data.warehouseList = res.data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchData()
|
||||||
|
getBrandList()
|
||||||
|
getWarehouseList()
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
uploadRef,
|
||||||
|
...toRefs(data),
|
||||||
|
handleSearch,
|
||||||
|
handleCurrentChange,
|
||||||
|
handleSizeChange,
|
||||||
|
fetchData,
|
||||||
|
handleAdd,
|
||||||
|
handleEdit,
|
||||||
|
commitOpa,
|
||||||
|
handleRemove,
|
||||||
|
handleUploadError,
|
||||||
|
handlePreview,
|
||||||
|
handleSuccess,
|
||||||
|
handleView,
|
||||||
|
handleSkuDelete,
|
||||||
|
resetForm,
|
||||||
|
toAddSku,
|
||||||
|
handleRemoveImg,
|
||||||
|
getBrandList,
|
||||||
|
getWarehouseList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.searchBox{
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 15px 0 0 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
.row{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 20%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
&.row1{
|
||||||
|
width: 25%;
|
||||||
|
}
|
||||||
|
.span{
|
||||||
|
display: block;
|
||||||
|
width: 80px;
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: right;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.right{
|
||||||
|
width: calc(100% - 100px);
|
||||||
|
}
|
||||||
|
.wid100{
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.opaBox{
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
.imgBox{
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
.el-image{
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.skuBox{
|
||||||
|
border: 1px solid #e5e5e5;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 15px 0;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
background-color: #f3f3f3;
|
||||||
|
.tit{
|
||||||
|
padding-left: 40px;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 15px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
358
src/views/commodity/sku.vue
Normal file
358
src/views/commodity/sku.vue
Normal file
@ -0,0 +1,358 @@
|
|||||||
|
<template>
|
||||||
|
<div class="pageBox">
|
||||||
|
<div class="searchBox">
|
||||||
|
<div class="row">
|
||||||
|
<span class="span">商品:</span>
|
||||||
|
<div class="right">
|
||||||
|
<el-select v-model="filter.goods_id" placeholder="请选择" clearable filterable class="wid100">
|
||||||
|
<el-option v-for="it in goodsList" :key="it.id" :label="it.title" :value="it.id" />
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<span class="span">规格名称:</span>
|
||||||
|
<div class="right"><el-input v-model="filter.title" class="wid100" clearable></el-input></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<span class="span">规格编码:</span>
|
||||||
|
<div class="right"><el-input v-model="filter.sku_code" class="wid100" clearable></el-input></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<span class="span">仓库:</span>
|
||||||
|
<div class="right">
|
||||||
|
<el-select v-model="filter.warehouse_id" placeholder="请选择" clearable class="wid100" filterable>
|
||||||
|
<el-option v-for="it in warehouseList" :key="it.id" :label="it.name" :value="it.id" />
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<span class="span">状态:</span>
|
||||||
|
<div class="right">
|
||||||
|
<el-select v-model="filter.status" placeholder="请选择" clearable class="wid100">
|
||||||
|
<el-option label="启用" :value="1" />
|
||||||
|
<el-option label="不启用" :value="0" />
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<span class="span"></span>
|
||||||
|
<div class="right"><el-button type="primary" @click="handleSearch"><el-icon><Search /></el-icon> 筛选</el-button></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-card shadow="never">
|
||||||
|
<div class="opaBox">
|
||||||
|
<el-button type="primary" @click="handleAdd"><el-icon><Plus /></el-icon> 新增</el-button>
|
||||||
|
<!-- <el-button type="warning" @click="handleExport"><span class="iconfont icon-daochu"></span> 导出</el-button> -->
|
||||||
|
</div>
|
||||||
|
<el-table :data="tableList" style="width: 100%" border v-loading="loading">
|
||||||
|
<el-table-column prop="id" label="ID" width="80" align="center" />
|
||||||
|
<el-table-column prop="goods.title" label="商品名称" align="center" />
|
||||||
|
<el-table-column prop="goods.goods_code" label="商品货号" align="center" />
|
||||||
|
<el-table-column prop="title" label="规格名称" align="center" />
|
||||||
|
<el-table-column prop="sku_code" label="规格编码" align="center" />
|
||||||
|
<el-table-column prop="actual_inventory" label="实际库存" align="center" />
|
||||||
|
<el-table-column prop="total_lock_num" label="总锁定库存" align="center" />
|
||||||
|
<el-table-column prop="lock_in_stock" label="运营锁定库存" align="center" />
|
||||||
|
<el-table-column prop="after_sale_stock" label="售后锁定库存" align="center" />
|
||||||
|
<el-table-column prop="available_inventory" label="可售库存" align="center" />
|
||||||
|
<el-table-column prop="warehouse.name" label="仓库" align="center" />
|
||||||
|
<el-table-column label="状态" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<span>{{ scope.row.status ? '启用' : '不启用' }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="created_at" label="添加时间" align="center" />
|
||||||
|
<el-table-column label="操作" align="center" width="150">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button type="primary" circle @click="handleEdit(scope.row)"><el-icon><Edit /></el-icon></el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div class="page-pagination">
|
||||||
|
<el-pagination
|
||||||
|
:current-page="page"
|
||||||
|
background
|
||||||
|
layout="prev, pager, next, sizes, total"
|
||||||
|
:total="total"
|
||||||
|
:page-sizes="[10, 50, 100]"
|
||||||
|
:page-size="pageSize"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange"></el-pagination>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-dialog v-model="showDialog" width="600px" :title="opaType == 'add' ? '新增' : '编辑' ">
|
||||||
|
<el-form label-position="right" label-width="110px">
|
||||||
|
<el-form-item label="所属商品:">
|
||||||
|
<el-select v-model="itemInfo.goods_id" placeholder="请选择" clearable filterable :disabled="role && opaType == 'edit'">
|
||||||
|
<el-option v-for="it in goodsList" :key="it.id" :label="it.title" :value="it.id" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="规格名称:">
|
||||||
|
<el-input v-model="itemInfo.title" clearable :disabled="role && opaType == 'edit'"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="规格编码:">
|
||||||
|
<el-input v-model="itemInfo.sku_code" clearable :disabled="role && opaType == 'edit'"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="仓库:">
|
||||||
|
<el-select v-model="itemInfo.warehouse_id" placeholder="请选择" clearable filterable :disabled="role && opaType == 'edit'">
|
||||||
|
<el-option v-for="it in warehouseList" :key="it.id" :label="it.name" :value="it.id" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="总锁定库存:">
|
||||||
|
<el-input v-model="itemInfo.total_lock_num" disabled></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="运营锁定库存:">
|
||||||
|
<el-input v-model="itemInfo.lock_in_stock" clearable></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="售后锁定库存:">
|
||||||
|
<el-input v-model="itemInfo.after_sale_stock" clearable :disabled="role && opaType == 'edit'"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="状态:">
|
||||||
|
<el-radio-group v-model="itemInfo.status" :disabled="role && opaType == 'edit'">
|
||||||
|
<el-radio :label="1">启用</el-radio>
|
||||||
|
<el-radio :label="0">不启用</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="赠品:">
|
||||||
|
<el-radio-group v-model="itemInfo.gift" :disabled="role && opaType == 'edit'">
|
||||||
|
<el-radio :label="0">不是</el-radio>
|
||||||
|
<el-radio :label="1">是</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="排序:">
|
||||||
|
<el-input-number v-model="itemInfo.sort" :min="0" :disabled="role && opaType == 'edit'" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer" v-if="opaType != 'view'">
|
||||||
|
<el-button @click="showDialog = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="commitOpa()" :loading="opa_loading">确定</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 预览图片 -->
|
||||||
|
<el-dialog v-model="picVisible" center width="800px">
|
||||||
|
<img :src="dialogImageUrl" style="max-width: 700px; margin: 0 auto;display: block;" />
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { onMounted, reactive, toRefs } from "vue"
|
||||||
|
import { get, post } from "@/api/request"
|
||||||
|
import { Search, Plus, Edit, ZoomIn, Delete } from '@element-plus/icons'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { parseErrors } from 'components/common'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Search, Plus, Edit, ZoomIn, Delete
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const data = reactive({
|
||||||
|
filter: {},
|
||||||
|
tableList: [],
|
||||||
|
page: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0,
|
||||||
|
loading: false,
|
||||||
|
opaType: '',
|
||||||
|
showDialog: false,
|
||||||
|
opa_loading: false,
|
||||||
|
warehouseList: [],
|
||||||
|
goodsList: [],
|
||||||
|
itemInfo: {},
|
||||||
|
role: localStorage.getItem('roleName') == '爆品运营'
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleSearch() {
|
||||||
|
data.page = 1
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchData = () => {
|
||||||
|
data.loading = true
|
||||||
|
let params = {
|
||||||
|
page: data.page,
|
||||||
|
pageSize: data.pageSize,
|
||||||
|
service_id: data.service_id,
|
||||||
|
...data.filter
|
||||||
|
}
|
||||||
|
get(`/api/goods-skus`, params).then((res) => {
|
||||||
|
data.tableList = res.data
|
||||||
|
data.total = res.meta.total
|
||||||
|
data.loading = false
|
||||||
|
}).catch(() => {
|
||||||
|
data.loading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCurrentChange(e) {
|
||||||
|
data.page = e
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSizeChange(e) {
|
||||||
|
data.page = 1
|
||||||
|
data.pageSize = e
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleAdd() {
|
||||||
|
data.opaType = 'add'
|
||||||
|
data.itemInfo = {
|
||||||
|
status: 1,
|
||||||
|
sort: 0,
|
||||||
|
gift: 0
|
||||||
|
}
|
||||||
|
data.showDialog = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleEdit(item) {
|
||||||
|
data.opaType = 'edit'
|
||||||
|
data.itemInfo = JSON.parse(JSON.stringify(item))
|
||||||
|
data.showDialog = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function commitOpa() {
|
||||||
|
data.opa_loading = true
|
||||||
|
let params = {
|
||||||
|
...data.itemInfo
|
||||||
|
}
|
||||||
|
if(data.opaType == 'add') {
|
||||||
|
post(`/api/goods-skus`, params).then(() => {
|
||||||
|
data.page = 1
|
||||||
|
fetchData()
|
||||||
|
ElMessage({ type: 'success', message: '新增成功' })
|
||||||
|
data.showDialog = false
|
||||||
|
data.opa_loading = false
|
||||||
|
}).catch((err) => {
|
||||||
|
data.opa_loading = false
|
||||||
|
parseErrors(err.response.data.errors)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
if(data.role) {
|
||||||
|
params = {
|
||||||
|
lock_in_stock: data.itemInfo.lock_in_stock
|
||||||
|
}
|
||||||
|
post(`/api/goods/sku/lockInStock/${data.itemInfo.id}`, params, 'PUT').then(() => {
|
||||||
|
fetchData()
|
||||||
|
ElMessage({ type: 'success', message: '编辑成功' })
|
||||||
|
data.showDialog = false
|
||||||
|
data.opa_loading = false
|
||||||
|
}).catch((err) => {
|
||||||
|
data.opa_loading = false
|
||||||
|
parseErrors(err.response.data.errors)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
post(`/api/goods-skus/${data.itemInfo.id}`, params, 'PUT').then(() => {
|
||||||
|
fetchData()
|
||||||
|
ElMessage({ type: 'success', message: '编辑成功' })
|
||||||
|
data.showDialog = false
|
||||||
|
data.opa_loading = false
|
||||||
|
}).catch((err) => {
|
||||||
|
data.opa_loading = false
|
||||||
|
parseErrors(err.response.data.errors)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getWarehouseList() {
|
||||||
|
get(`/api/all/warehouses`).then((res) => {
|
||||||
|
data.warehouseList = res.data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGoodsList() {
|
||||||
|
get(`/api/all/goods`).then((res) => {
|
||||||
|
data.goodsList = res.data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchData()
|
||||||
|
getWarehouseList()
|
||||||
|
getGoodsList()
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toRefs(data),
|
||||||
|
handleSearch,
|
||||||
|
handleCurrentChange,
|
||||||
|
handleSizeChange,
|
||||||
|
fetchData,
|
||||||
|
handleAdd,
|
||||||
|
handleEdit,
|
||||||
|
commitOpa,
|
||||||
|
getWarehouseList,
|
||||||
|
getGoodsList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.searchBox{
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 15px 0 0 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
.row{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 20%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
&.row1{
|
||||||
|
width: 25%;
|
||||||
|
}
|
||||||
|
.span{
|
||||||
|
display: block;
|
||||||
|
width: 80px;
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: right;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.right{
|
||||||
|
width: calc(100% - 100px);
|
||||||
|
}
|
||||||
|
.wid100{
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.opaBox{
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
.imgBox{
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
.el-image{
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.skuBox{
|
||||||
|
border: 1px solid #e5e5e5;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 15px 0;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
background-color: #f3f3f3;
|
||||||
|
.tit{
|
||||||
|
padding-left: 40px;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 15px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
43
src/views/home/index.vue
Normal file
43
src/views/home/index.vue
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<template>
|
||||||
|
<div class="pageBox">
|
||||||
|
<div class="oneBox">
|
||||||
|
<h3>今天也是充满希望的一天</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { onMounted, reactive, toRefs } from "vue"
|
||||||
|
import { get } from "@/api/request"
|
||||||
|
|
||||||
|
export default {
|
||||||
|
setup() {
|
||||||
|
const data = reactive({
|
||||||
|
numTotal: {}
|
||||||
|
})
|
||||||
|
|
||||||
|
const fetchData = () => {
|
||||||
|
// get(`/api/admin/home`).then((res) => {
|
||||||
|
// data.numTotal = res.data
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchData()
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toRefs(data),
|
||||||
|
fetchData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.oneBox{
|
||||||
|
padding: 20px;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
357
src/views/layout/index.vue
Normal file
357
src/views/layout/index.vue
Normal file
@ -0,0 +1,357 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-container class="home">
|
||||||
|
<!--侧边栏-->
|
||||||
|
<div class="home-aside" :class="hideAside ? 'hideAside' : '' ">
|
||||||
|
<el-scrollbar wrap-class="scrollbar-wrapper">
|
||||||
|
<Menu :menu-data="menuData" :is-collapse="hideAside"></Menu>
|
||||||
|
</el-scrollbar>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-main class="home-main" :class="hideAside ? 'hideAside' : '' ">
|
||||||
|
<div class="main">
|
||||||
|
<div class="topHead">
|
||||||
|
<div class="expand" @click="hideAside = !hideAside">
|
||||||
|
<el-icon v-if="hideAside"><Expand /></el-icon>
|
||||||
|
<el-icon v-else><Fold /></el-icon>
|
||||||
|
</div>
|
||||||
|
<!-- <Breadcrumb class="breadcrumb" separator="/"></Breadcrumb> -->
|
||||||
|
<el-dropdown @command="(command) => { handleCommand(command); }">
|
||||||
|
<div style="display: flex;padding-right: 20px;">
|
||||||
|
<span class="el-dropdown-link"><el-icon style="vertical-align: bottom;"><UserFilled /></el-icon> {{ name }}</span>
|
||||||
|
<el-icon><ArrowDown /></el-icon>
|
||||||
|
</div>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item command="1"><el-icon><SwitchButton /></el-icon>退出登录</el-dropdown-item>
|
||||||
|
<!-- <el-dropdown-item command="2"><el-icon><Unlock /></el-icon>修改密码</el-dropdown-item> -->
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
</div>
|
||||||
|
<router-view />
|
||||||
|
|
||||||
|
<!-- <router-view v-slot="{ Component }">
|
||||||
|
<Transition mode="out-in">
|
||||||
|
<component :is="Component" />
|
||||||
|
</Transition>
|
||||||
|
</router-view> -->
|
||||||
|
|
||||||
|
<!-- <router-view v-slot="{ Component, route }">
|
||||||
|
<transition mode="out-in" appear>
|
||||||
|
<component :is="Component" :key="route.path" />
|
||||||
|
</transition>
|
||||||
|
</router-view> -->
|
||||||
|
</div>
|
||||||
|
</el-main>
|
||||||
|
</el-container>
|
||||||
|
|
||||||
|
<el-dialog v-model="showModify" title="修改密码" width="350px">
|
||||||
|
<el-form label-position="right" label-width="85px">
|
||||||
|
<el-form-item label="修改密码:">
|
||||||
|
<el-input v-model="item.password" type="password"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="确认密码:">
|
||||||
|
<el-input v-model="item.password_confirmation" type="password"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="showModify = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="commitModify">确定</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { onMounted, reactive, toRefs, watch } from 'vue'
|
||||||
|
import { routes } from '@/router'
|
||||||
|
import Menu from './menu'
|
||||||
|
import Breadcrumb from '@/components/Breadcrumb'
|
||||||
|
import { SwitchButton, ArrowDown, Unlock, UserFilled, Expand, Fold } from '@element-plus/icons'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import { get, post } from "@/api/request"
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Menu,
|
||||||
|
Breadcrumb,
|
||||||
|
SwitchButton,
|
||||||
|
ArrowDown,
|
||||||
|
Unlock,
|
||||||
|
Expand,
|
||||||
|
Fold,
|
||||||
|
UserFilled
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const data = reactive({
|
||||||
|
title: '',
|
||||||
|
name: localStorage.getItem('userName'),
|
||||||
|
item: {},
|
||||||
|
menuData: [],
|
||||||
|
hideAside: false,
|
||||||
|
showModify: false
|
||||||
|
})
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
//监听路由名称
|
||||||
|
watch(
|
||||||
|
() => router.currentRoute.value.meta.title,
|
||||||
|
(toPath, old) => {
|
||||||
|
data.title = toPath
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deep: true,
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const NewRoutes = (() => {
|
||||||
|
const retRoutes = []
|
||||||
|
for (const value of routes) {
|
||||||
|
if (value.path === '/') {
|
||||||
|
if (value.children && value.children.length) {
|
||||||
|
// 将children中的路由记录往上提一层,方便<SidebarItem>渲染
|
||||||
|
for (const routeItem of value.children) {
|
||||||
|
const obj = Object.assign({}, routeItem)
|
||||||
|
obj.path = `/${routeItem.path}`
|
||||||
|
if(obj.name == 'manage') {
|
||||||
|
obj.hidden = localStorage.getItem('isSuper') == 0
|
||||||
|
}
|
||||||
|
retRoutes.push(obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
retRoutes.push(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(retRoutes)
|
||||||
|
return retRoutes
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleCommand = (type) => {
|
||||||
|
if (type == 1) {
|
||||||
|
loginOut()
|
||||||
|
} else if(type == 2) {
|
||||||
|
data.item = {}
|
||||||
|
data.showModify = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const commitModify = () => {
|
||||||
|
post('/api/staffs/password', data.item).then(() => {
|
||||||
|
ElMessage({ type: 'success', message: '修改成功' })
|
||||||
|
data.showModify = false
|
||||||
|
loginOut()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const loginOut = () => {
|
||||||
|
get('/api/auth/logout').then(() => {
|
||||||
|
localStorage.removeItem('token')
|
||||||
|
localStorage.removeItem('saveTime')
|
||||||
|
router.replace({
|
||||||
|
path: '/login'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
data.menuData = JSON.parse(localStorage.getItem('menusList'))
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toRefs(data),
|
||||||
|
handleCommand,
|
||||||
|
commitModify
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.breadcrumb{
|
||||||
|
padding: 0 20px;
|
||||||
|
}
|
||||||
|
.topHead{
|
||||||
|
height: 45px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
border-bottom: 1px solid #E5E5E5;
|
||||||
|
background-color: #fff;
|
||||||
|
.expand{
|
||||||
|
padding: 10px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 16px;
|
||||||
|
margin-right: 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.home {
|
||||||
|
position: relative;
|
||||||
|
height: 100vh;
|
||||||
|
width: 100vw;
|
||||||
|
overflow: hidden;
|
||||||
|
display: block;
|
||||||
|
&-header {
|
||||||
|
background-color: #fff;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
// position: absolute;
|
||||||
|
border-bottom: 1px solid #e5e5e5;
|
||||||
|
box-sizing: border-box;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0;
|
||||||
|
&-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
// justify-content: center;
|
||||||
|
padding-left: 17px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: #FAFAFA;
|
||||||
|
color: #fff;
|
||||||
|
width: 100%;
|
||||||
|
height: 40px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-right {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex: 1;
|
||||||
|
.expand{
|
||||||
|
padding: 10px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
&-btn {
|
||||||
|
height: 40px;
|
||||||
|
// border-bottom: 1px solid #ececec;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-aside {
|
||||||
|
height: 100%;
|
||||||
|
width: 200px;
|
||||||
|
background-color: #F9FAFC;
|
||||||
|
transition: width 0.3s;
|
||||||
|
position: fixed;
|
||||||
|
overflow: hidden;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1;
|
||||||
|
.home-header-title{
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
&.hideAside{
|
||||||
|
width: 64px;
|
||||||
|
.home-header-title{
|
||||||
|
padding: 0;
|
||||||
|
width: 64px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&-menu {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-main {
|
||||||
|
position: relative;
|
||||||
|
transition: margin-left 0.28s;
|
||||||
|
margin-left: 200px;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 100%;
|
||||||
|
background-color: #ecf0f5;
|
||||||
|
&.hideAside{
|
||||||
|
// width: calc(100% - 64px);
|
||||||
|
margin-left: 64px;
|
||||||
|
}
|
||||||
|
.main {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.el-icon {
|
||||||
|
&:hover{
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.el-menu{
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
.v-enter-active,
|
||||||
|
.v-leave-active {
|
||||||
|
transition: all 0.5s ease;
|
||||||
|
}
|
||||||
|
.v-leave-to,
|
||||||
|
.v-enter-from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(-30px);
|
||||||
|
}
|
||||||
|
.v-leave-from,
|
||||||
|
.v-enter-to {
|
||||||
|
transform: translateX(0px);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.home-header-right-btn .el-dropdown--small{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.home-aside .el-aside{
|
||||||
|
height: calc(100vh - 40px);
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
.el-menu-item{
|
||||||
|
height: 50px !important;
|
||||||
|
line-height: 50px !important;
|
||||||
|
}
|
||||||
|
.el-sub-menu__title{
|
||||||
|
width: 200px;
|
||||||
|
height: 50px !important;
|
||||||
|
line-height: 50px !important;
|
||||||
|
}
|
||||||
|
.hideAside .el-sub-menu__title{
|
||||||
|
width: 64px;
|
||||||
|
}
|
||||||
|
.home-aside .el-sub-menu .el-sub-menu__icon-arrow{
|
||||||
|
width: 12px;
|
||||||
|
}
|
||||||
|
.el-sub-menu .el-menu-item{
|
||||||
|
padding: 0 0 0 45px !important;
|
||||||
|
}
|
||||||
|
.el-menu--popup .el-sub-menu .el-menu-item{
|
||||||
|
padding: 0 0 0 20px !important;
|
||||||
|
}
|
||||||
|
.el-menu .el-sub-menu .el-menu .el-sub-menu .el-menu .el-menu-item{
|
||||||
|
padding: 0 0 0 55px !important;
|
||||||
|
}
|
||||||
|
.el-menu--popup .el-sub-menu__icon-arrow{
|
||||||
|
width: 12px !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
43
src/views/layout/menu.vue
Normal file
43
src/views/layout/menu.vue
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<template>
|
||||||
|
<el-menu :default-active="activerouter" class="el-menu-vertical-demo" background-color="#FAFAFA"
|
||||||
|
text-color="#232323" router unique-opened :collapse="isCollapse" :collapse-transition="false">
|
||||||
|
<menu-tree :menu-data="menuData" />
|
||||||
|
</el-menu>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { onMounted, reactive, toRefs } from 'vue'
|
||||||
|
import menuTree from './menuTree'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
menuData: Array,
|
||||||
|
isCollapse: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
menuTree,
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const data = reactive({
|
||||||
|
activerouter: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
let defaultMenu = window.location.hash.substr(
|
||||||
|
window.location.hash.indexOf("/")
|
||||||
|
)
|
||||||
|
data.activerouter = defaultMenu
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toRefs(data)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
</style>
|
||||||
58
src/views/layout/menuTree.vue
Normal file
58
src/views/layout/menuTree.vue
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<template>
|
||||||
|
<template v-for="(menu, i) in menuData" :key="menu.uri">
|
||||||
|
<el-menu-item :index="menu.uri" v-if="!menu.children">
|
||||||
|
<div class="icon" v-if="menu.parent_id == 0">
|
||||||
|
<img v-if="menu.icon" :src="menu.icon">
|
||||||
|
</div>
|
||||||
|
<template #title><span :class="menu.parent_id == 0 ? 'span' : ''">{{menu.title}}</span></template>
|
||||||
|
</el-menu-item>
|
||||||
|
<el-sub-menu :index="menu.uri" v-else popper-class="popper-meun">
|
||||||
|
<template #title>
|
||||||
|
<div class="icon" v-if="menu.parent_id == 0">
|
||||||
|
<img v-if="menu.icon" :src="menu.icon">
|
||||||
|
</div>
|
||||||
|
<span :class="menu.parent_id == 0 ? 'span' : 'sp1'">{{menu.title}}</span>
|
||||||
|
</template>
|
||||||
|
<menu-tree :menu-data="menu.children" />
|
||||||
|
</el-sub-menu>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
menuData: Array
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.icon{
|
||||||
|
width: 24px;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
position: absolute;
|
||||||
|
left: 20px;
|
||||||
|
top: 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
.icon img{
|
||||||
|
width: 17px;
|
||||||
|
}
|
||||||
|
.sp1{
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.el-menu-item ::v-deep .span, ::v-deep .el-sub-menu__title .span{
|
||||||
|
padding-left: 30px;
|
||||||
|
position: relative;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
45
src/views/login/404.vue
Normal file
45
src/views/login/404.vue
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<template>
|
||||||
|
<div class="whole">
|
||||||
|
<div class="notfund">
|
||||||
|
<img class="img" src="@/assets/imgs/404.jpg" />
|
||||||
|
<div class="box">
|
||||||
|
<p>抱歉,您访问的页面找不到了/(ㄒoㄒ)/~~</p>
|
||||||
|
<el-button type="primary" @click="$router.back()">返回上一页</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
setup() {
|
||||||
|
const router = useRouter()
|
||||||
|
function toBackPage() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
toBackPage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.whole{
|
||||||
|
width: 100%;
|
||||||
|
height: 100vh;
|
||||||
|
.notfund{
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.box{
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
212
src/views/login/index.vue
Normal file
212
src/views/login/index.vue
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
<template>
|
||||||
|
<div class="login">
|
||||||
|
<div class="login-box">
|
||||||
|
<div class="login-box-title">管理系统</div>
|
||||||
|
<el-input v-model="username" placeholder="用户名" class="account" />
|
||||||
|
<el-input class="password" v-model="password" placeholder="密码" show-password @keyup.enter.native="Login" />
|
||||||
|
<el-button type="primary" @click="Login" :loading="loading">登录</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { reactive, toRefs, onMounted } from 'vue'
|
||||||
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { post } from '@/api/request'
|
||||||
|
import axios from 'axios'
|
||||||
|
import { whiteList } from '@/router/white'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
setup() {
|
||||||
|
const router = useRouter()
|
||||||
|
const data = reactive({
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
loading: false
|
||||||
|
})
|
||||||
|
|
||||||
|
async function Login() {
|
||||||
|
if(!data.username || !data.password) {
|
||||||
|
return ElMessage({ type: 'warning', message: '请输入用户名及密码' })
|
||||||
|
}
|
||||||
|
data.loading = true
|
||||||
|
await post('/api/auth/login', { username: data.username, password: data.password }).then(async(res) => {
|
||||||
|
if (res.access_token) {
|
||||||
|
const token = res.token_type + ' ' + res.access_token
|
||||||
|
localStorage.setItem('token', token)
|
||||||
|
localStorage.setItem('saveTime', Date.now()) // 存储时间
|
||||||
|
localStorage.setItem('expires_in', (res.expires_in - 10) * 1000) // 失效时间
|
||||||
|
|
||||||
|
if(res.user.role_name == '销售主账号') {
|
||||||
|
localStorage.setItem('roleName', '销售')
|
||||||
|
localStorage.setItem('roleNamex', '销售x')
|
||||||
|
} else {
|
||||||
|
localStorage.setItem('roleName', res.user.role_name)
|
||||||
|
localStorage.setItem('roleNamex', '')
|
||||||
|
}
|
||||||
|
localStorage.setItem('userName', data.username)
|
||||||
|
await getMenusList()
|
||||||
|
router.replace('/home')
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
data.loading = false
|
||||||
|
}, 1000)
|
||||||
|
}).catch(() => {
|
||||||
|
data.loading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseToken(token) {
|
||||||
|
axios.get(`/api/authInfo`, {
|
||||||
|
headers: {
|
||||||
|
'Authorization': token,
|
||||||
|
'content-type': 'application/json'
|
||||||
|
}
|
||||||
|
}).then(async(res) => {
|
||||||
|
const token = res.data.token_type + ' ' + res.data.access_token
|
||||||
|
localStorage.setItem('token', token)
|
||||||
|
localStorage.setItem('saveTime', Date.now()) // 存储时间
|
||||||
|
localStorage.setItem('expires_in', (res.data.expires_in - 10) * 1000) // 失效时间
|
||||||
|
|
||||||
|
if(res.data.user.role_name == '销售主账号') {
|
||||||
|
localStorage.setItem('roleName', '销售')
|
||||||
|
localStorage.setItem('roleNamex', '销售x')
|
||||||
|
} else {
|
||||||
|
localStorage.setItem('roleName', res.data.user.role_name)
|
||||||
|
localStorage.setItem('roleNamex', '')
|
||||||
|
}
|
||||||
|
localStorage.setItem('userName', res.data.user.username)
|
||||||
|
await getMenusList()
|
||||||
|
router.replace('/home')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 获取用户权限对应的侧边栏菜单
|
||||||
|
let menus = []
|
||||||
|
async function getMenusList() {
|
||||||
|
await post('/api/role/menus').then((res) => {
|
||||||
|
localStorage.setItem('menusList', JSON.stringify(res.data))
|
||||||
|
res.data.forEach(async(item) => {
|
||||||
|
if(item.children) {
|
||||||
|
await fn(item.children)
|
||||||
|
} else {
|
||||||
|
menus.push({
|
||||||
|
path: item.uri,
|
||||||
|
name: getRouterName(item.uri),
|
||||||
|
full_path: getRouterFullPath(item.uri)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
localStorage.setItem('menus', JSON.stringify(menus))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRouterName(uri) {
|
||||||
|
let name = ''
|
||||||
|
whiteList.forEach((item) => {
|
||||||
|
if(uri == item.path) {
|
||||||
|
name = item.name
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRouterFullPath(uri) {
|
||||||
|
let full_path = ''
|
||||||
|
whiteList.forEach((item) => {
|
||||||
|
if(uri == item.path) {
|
||||||
|
full_path = item.full_path
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return full_path
|
||||||
|
}
|
||||||
|
|
||||||
|
function fn(item) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
item.forEach(async(item) => {
|
||||||
|
if(item.children) {
|
||||||
|
await fn(item.children)
|
||||||
|
} else {
|
||||||
|
menus.push({
|
||||||
|
path: item.uri,
|
||||||
|
name: getRouterName(item.uri),
|
||||||
|
full_path: getRouterFullPath(item.uri)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if(route.query.token) {
|
||||||
|
parseToken(route.query.token)
|
||||||
|
} else {
|
||||||
|
let token = localStorage.getItem('token')
|
||||||
|
let saveTime = localStorage.getItem('saveTime')
|
||||||
|
let expires_in = localStorage.getItem('expires_in')
|
||||||
|
if(token && saveTime && expires_in) {
|
||||||
|
if((Date.now() > saveTime + expires_in)) {
|
||||||
|
localStorage.removeItem('token')
|
||||||
|
localStorage.removeItem('saveTime')
|
||||||
|
localStorage.removeItem('expires_in')
|
||||||
|
router.replace({ path: '/login' })
|
||||||
|
} else {
|
||||||
|
goToPage()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
router.replace({ path: '/login' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function goToPage() {
|
||||||
|
router.replace({ path: '/home' })
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toRefs(data),
|
||||||
|
Login,
|
||||||
|
goToPage,
|
||||||
|
parseToken
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.login {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
&-box {
|
||||||
|
width: 300px;
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: 30%;
|
||||||
|
transform: translate(-30%, -50%);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
&-title {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 25px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
.el-input {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.wechat{
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
.img{
|
||||||
|
width: 30px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
25
src/views/permission/menu.js
Normal file
25
src/views/permission/menu.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import {reactive} from "vue";
|
||||||
|
|
||||||
|
export default function menu() {
|
||||||
|
const menuData = reactive({
|
||||||
|
treeData: '',
|
||||||
|
treeDataValue: {},
|
||||||
|
defaultProps: {
|
||||||
|
parent: 'parent_id', // 父级唯一标识
|
||||||
|
value: 'id', // 唯一标识
|
||||||
|
label: 'title', // 标签显示
|
||||||
|
children: 'children' // 子级
|
||||||
|
},
|
||||||
|
})
|
||||||
|
// 选择
|
||||||
|
function handleNodeClick (data){
|
||||||
|
menuData.treeData = data.id
|
||||||
|
menuData.treeDataValue = data
|
||||||
|
console.log(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
menuData,
|
||||||
|
handleNodeClick
|
||||||
|
}
|
||||||
|
}
|
||||||
263
src/views/permission/menus.vue
Normal file
263
src/views/permission/menus.vue
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
<template>
|
||||||
|
<div class="pageBox">
|
||||||
|
<el-card shadow="never">
|
||||||
|
<div class="opaBox">
|
||||||
|
<el-button type="primary" @click="addBtn"><el-icon><Plus /></el-icon> 新增</el-button>
|
||||||
|
</div>
|
||||||
|
<el-table :data="tableData" border style="width: 100%" row-key="id" v-loading="loading">
|
||||||
|
<el-table-column prop="title" label="名称" width="200" />
|
||||||
|
<el-table-column prop="id" label="ID" width="150" />
|
||||||
|
<el-table-column prop="order" label="排序" sortable width="150" />
|
||||||
|
<el-table-column prop="uri" label="路径" />
|
||||||
|
<el-table-column label="图标">
|
||||||
|
<template #default="scope">
|
||||||
|
<div class="iconBox" v-if="scope.row.icon">
|
||||||
|
<el-image :src="scope.row.icon" class="icon"></el-image>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="状态" width="180">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-switch
|
||||||
|
v-model="scope.row.is_enabled"
|
||||||
|
:active-text="scope.row.is_enabled ? '启用' : ''"
|
||||||
|
@change="changeType(scope.row)" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" fixed="right" width="200" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button type="primary" @click="editBtn(scope.row)">编辑</el-button>
|
||||||
|
<el-button type="danger" @click="delBtn(scope.row.id)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-dialog v-model="addDialog" :title="item.id ? '编辑菜单' : '添加菜单'" width="500px">
|
||||||
|
<el-form :model="item" label-position="right" label-width="100px">
|
||||||
|
<el-form-item label="上级菜单">
|
||||||
|
<el-select v-model="treeDataValue.id" placeholder="请选择" clearable>
|
||||||
|
<el-option :value="treeDataValue.id" :label="treeDataValue.title" style="height: auto;">
|
||||||
|
<el-tree
|
||||||
|
:data="tableData"
|
||||||
|
ref="tree"
|
||||||
|
node-key="id"
|
||||||
|
:highlight-current="true"
|
||||||
|
:props="defaultProps"
|
||||||
|
@node-click="handleNodeClick" />
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="菜单名称">
|
||||||
|
<el-input v-model="item.title" style="width: 200px;" clearable></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="菜单图标">
|
||||||
|
<el-upload
|
||||||
|
class="avatar-uploader"
|
||||||
|
action="/api/upload/img"
|
||||||
|
:on-error="handleUploadError"
|
||||||
|
:headers="headers"
|
||||||
|
:on-remove="handleRemove"
|
||||||
|
:on-preview="handlePreview"
|
||||||
|
:on-success="handleSuccess"
|
||||||
|
:file-list="iconImage"
|
||||||
|
:show-file-list="true"
|
||||||
|
list-type="picture-card"
|
||||||
|
:limmit="1"
|
||||||
|
accept=".png,.jpg,.gif">
|
||||||
|
<el-icon><Plus /></el-icon>
|
||||||
|
</el-upload>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="路由地址">
|
||||||
|
<el-input v-model="item.uri" style="width: 200px;" clearable></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="排序">
|
||||||
|
<el-input v-model="item.order" style="width: 200px;" clearable></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="状态">
|
||||||
|
<el-switch v-model="item.is_enabled" :active-text="item.is_enabled ? '启用' : ''" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="close">取消</el-button>
|
||||||
|
<el-button type="primary" @click="submitBtn">确定</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 预览图片 -->
|
||||||
|
<el-dialog v-model="picVisible" center width="800px">
|
||||||
|
<img :src="dialogImageUrl" style="max-width: 700px; margin: 0 auto;display: block;" />
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { onMounted, reactive, toRefs } from 'vue'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import { get, post } from '@/api/request'
|
||||||
|
import menu from './menu'
|
||||||
|
import { Plus } from '@element-plus/icons'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Plus
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const data = reactive({
|
||||||
|
tableData: [],
|
||||||
|
loading: false,
|
||||||
|
item: {},
|
||||||
|
addDialog: false,
|
||||||
|
headers: {
|
||||||
|
Authorization: localStorage.getItem('token'),
|
||||||
|
'Shop-Id': localStorage.getItem('shopId') || ''
|
||||||
|
},
|
||||||
|
dialogImageUrl: '',
|
||||||
|
picVisible: false,
|
||||||
|
iconImage: []
|
||||||
|
})
|
||||||
|
|
||||||
|
function getMenuList() {
|
||||||
|
data.loading = true
|
||||||
|
get('/api/menu').then((res) => {
|
||||||
|
data.tableData = res.data
|
||||||
|
data.loading = false
|
||||||
|
}).catch(() => {
|
||||||
|
data.loading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const { menuData, handleNodeClick } = menu()
|
||||||
|
|
||||||
|
function addBtn() {
|
||||||
|
menuData.treeDataValue = {
|
||||||
|
id: '',
|
||||||
|
title: ''
|
||||||
|
}
|
||||||
|
data.item = {
|
||||||
|
is_enabled: true,
|
||||||
|
icon: ''
|
||||||
|
}
|
||||||
|
data.iconImage = []
|
||||||
|
data.addDialog = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function editBtn(item) {
|
||||||
|
data.item = {
|
||||||
|
...item
|
||||||
|
}
|
||||||
|
data.iconImage = item.icon ? [{url: item.icon}] : []
|
||||||
|
menuData.treeDataValue = item.parent || {}
|
||||||
|
data.addDialog = true
|
||||||
|
}
|
||||||
|
|
||||||
|
async function submitBtn() {
|
||||||
|
data.item.parent_id = menuData.treeDataValue.id || 0
|
||||||
|
let res = ''
|
||||||
|
if (data.item.id) {
|
||||||
|
res = await post(`/api/menu/${data.item.id}`, data.item, 'put')
|
||||||
|
} else {
|
||||||
|
res = await post('/api/menu', data.item)
|
||||||
|
}
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({ type: 'success', message: data.item.id ? '编辑成功' : '添加成功' })
|
||||||
|
data.addDialog = false
|
||||||
|
getMenuList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function changeType(item) {
|
||||||
|
data.item = { ...item }
|
||||||
|
await post(`/api/menu/${data.item.id}`, data.item, 'put').then(() => {
|
||||||
|
ElMessage({ type: 'success', message: '编辑成功' })
|
||||||
|
getMenuList()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function delBtn(id) {
|
||||||
|
ElMessageBox.confirm('确定删除该菜单吗?', '警告', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
post(`/api/menu/${id}`, {}, 'delete').then(() => {
|
||||||
|
ElMessage({ type: 'success', message: '删除成功' })
|
||||||
|
getMenuList()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function close() {
|
||||||
|
data.addDialog = false
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleUploadError(err) {
|
||||||
|
if(JSON.parse(err.message) && JSON.parse(err.message).message) {
|
||||||
|
ElMessage({ type: 'error', message: JSON.parse(err.message).message })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleRemove(res, ress) {
|
||||||
|
data.item.icon = ''
|
||||||
|
data.iconImage = []
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePreview(File) {
|
||||||
|
data.dialogImageUrl = File.url
|
||||||
|
data.picVisible = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSuccess(res) {
|
||||||
|
if (res.code === 0) {
|
||||||
|
data.iconImage = [{url: res.data.link}]
|
||||||
|
data.item.icon = res.data.link
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getMenuList()
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toRefs(data),
|
||||||
|
addBtn,
|
||||||
|
submitBtn,
|
||||||
|
close,
|
||||||
|
editBtn,
|
||||||
|
delBtn,
|
||||||
|
changeType,
|
||||||
|
...toRefs(menuData),
|
||||||
|
handleNodeClick,
|
||||||
|
handleUploadError,
|
||||||
|
handleSuccess,
|
||||||
|
handlePreview,
|
||||||
|
handleRemove
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.opaBox{
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
.iconBox{
|
||||||
|
background-color: rgba(0,0,0,0.5);
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 4px;
|
||||||
|
.icon{
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
margin: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
::v-deep .el-upload-list--picture-card .el-upload-list__item{
|
||||||
|
background-color: rgba(0,0,0,0.5);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
173
src/views/permission/role.vue
Normal file
173
src/views/permission/role.vue
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
<template>
|
||||||
|
<div class="pageBox">
|
||||||
|
<el-card shadow="never">
|
||||||
|
<el-table :data="tableData" border style="width: 100%" v-loading="loading">
|
||||||
|
<el-table-column prop="name" label="角色名" width="200" />
|
||||||
|
<el-table-column label="角色类型" width="100">
|
||||||
|
<template #default="scope">
|
||||||
|
<span v-if="scope.row.type === 0">主角色</span>
|
||||||
|
<span v-else>子角色</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="角色权限">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag v-for="i in scope.row.menus" :key="i.id" size="small" class="md-5">{{i.title}}</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" fixed="right" width="200" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button type="primary" @click="editBtn(scope.row)">编辑</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div class="page-pagination">
|
||||||
|
<el-pagination
|
||||||
|
:current-page="page"
|
||||||
|
background
|
||||||
|
layout="prev, pager, next, sizes, total"
|
||||||
|
:total="total"
|
||||||
|
:page-sizes="[10, 50, 100]"
|
||||||
|
:page-size="pageSize"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-dialog v-model="addDialog" :title="item.id ? '编辑角色' : '新增角色'" width="800px">
|
||||||
|
<el-form :model="item" label-width="90px">
|
||||||
|
<el-form-item label="角色名:">
|
||||||
|
<el-input v-model="item.name" style="width: 400px;" disabled></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="角色类型:">
|
||||||
|
<el-select v-model="item.type" placeholder="请选择" disabled>
|
||||||
|
<el-option label="主角色" :value="0"></el-option>
|
||||||
|
<el-option label="子角色" :value="1"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<div style="display: flex;">
|
||||||
|
<el-form-item label="菜单权限:">
|
||||||
|
<el-tree ref="tree"
|
||||||
|
:data="menuList"
|
||||||
|
show-checkbox
|
||||||
|
node-key="id"
|
||||||
|
:props="defaultProps"
|
||||||
|
:check-strictly="true"
|
||||||
|
:expand-on-click-node="false"
|
||||||
|
:default-checked-keys="item.menus" />
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="addDialog = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="submitBtn">确定</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { onMounted, reactive, toRefs, nextTick, ref } from 'vue'
|
||||||
|
import { get, post } from '@/api/request'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
setup() {
|
||||||
|
const data = reactive({
|
||||||
|
page: 1,
|
||||||
|
total: 0,
|
||||||
|
tableData: [],
|
||||||
|
loading: false,
|
||||||
|
item: {},
|
||||||
|
menuList: [],
|
||||||
|
addDialog: false,
|
||||||
|
defaultProps: {
|
||||||
|
parent: 'parent_id', // 父级唯一标识
|
||||||
|
value: 'id', // 唯一标识
|
||||||
|
label: 'title', // 标签显示
|
||||||
|
children: 'children', // 子级
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleCurrentChange(e) {
|
||||||
|
data.page = e
|
||||||
|
getRoleList()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSizeChange(e) {
|
||||||
|
data.page = 1
|
||||||
|
data.pageSize = e
|
||||||
|
getRoleList()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getRoleList() {
|
||||||
|
data.loading = true
|
||||||
|
await get('/api/role', { page: data.page, pageSize: 10 }).then((res) => {
|
||||||
|
data.tableData = res.data
|
||||||
|
data.total = res.meta.total
|
||||||
|
data.loading = false
|
||||||
|
}).catch(() => {
|
||||||
|
data.loading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function editBtn(item) {
|
||||||
|
data.item = { ...item }
|
||||||
|
let menu = []
|
||||||
|
item.menus.forEach((i) => {
|
||||||
|
menu.push(i.id)
|
||||||
|
})
|
||||||
|
data.item.menus = menu
|
||||||
|
await nextTick(async () => {
|
||||||
|
data.menuList = [...data.menuList]
|
||||||
|
data.addDialog = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let tree = ref(null)
|
||||||
|
function submitBtn() {
|
||||||
|
let params = {
|
||||||
|
roleId: data.item.id,
|
||||||
|
menus: tree.value.getCheckedKeys()
|
||||||
|
}
|
||||||
|
post('/api/role/storeMenus', params).then(() => {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '编辑成功'
|
||||||
|
})
|
||||||
|
data.addDialog = false
|
||||||
|
getRoleList()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMenuList() {
|
||||||
|
get('/api/menu').then((res) => {
|
||||||
|
data.menuList = res.data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getRoleList()
|
||||||
|
getMenuList()
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
tree,
|
||||||
|
...toRefs(data),
|
||||||
|
handleCurrentChange,
|
||||||
|
handleSizeChange,
|
||||||
|
getMenuList,
|
||||||
|
editBtn,
|
||||||
|
submitBtn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.md-5{
|
||||||
|
margin: 3px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
527
src/views/purchase/index.vue
Normal file
527
src/views/purchase/index.vue
Normal file
@ -0,0 +1,527 @@
|
|||||||
|
<template>
|
||||||
|
<div class="pageBox">
|
||||||
|
<!-- <div class="searchBox">
|
||||||
|
<div class="row">
|
||||||
|
<span class="span">商品名称:</span>
|
||||||
|
<div class="right"><el-input v-model="goods_name" class="wid100" clearable></el-input></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<el-button type="primary" @click="handleSearch"><el-icon><Search /></el-icon> 筛选</el-button>
|
||||||
|
</div>
|
||||||
|
</div> -->
|
||||||
|
|
||||||
|
<el-card shadow="never">
|
||||||
|
<div class="opaBox">
|
||||||
|
<el-button type="primary" @click="handleAdd"><el-icon><Plus /></el-icon> 新增</el-button>
|
||||||
|
<!-- <el-button type="warning" @click="handleExport"><span class="iconfont icon-daochu"></span> 导出</el-button> -->
|
||||||
|
</div>
|
||||||
|
<el-table :data="goodsList" style="width: 100%" border v-loading="loading">
|
||||||
|
<el-table-column prop="id" label="ID" width="80" align="center" />
|
||||||
|
<el-table-column prop="type" label="采购类型" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<span>{{ Type[scope.row.type] }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="supplier.name" label="供应商" align="center" />
|
||||||
|
<el-table-column prop="warehouse.name" label="仓库" align="center" />
|
||||||
|
<el-table-column prop="batch_number" label="批次" align="center" />
|
||||||
|
<el-table-column label="采购单" align="center" min-width="150">
|
||||||
|
<template #default="scope">
|
||||||
|
<div class="imgBox">
|
||||||
|
<el-image :z-index="9999" v-for="(item, index) in scope.row.images" :key="index" :src="item" :hide-on-click-modal="true" :preview-src-list="[item]" fit="cover" :preview-teleported="true" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="attachments" label="附件">
|
||||||
|
<template #default="scope">
|
||||||
|
<div class="imgBox">
|
||||||
|
<a :href="scope.row.attachments" target="_blank" style="word-break: break-all;">{{scope.row.attachments}}</a>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="in_put_time" label="入仓时间" align="center" />
|
||||||
|
<el-table-column prop="purchasing_agent" label="采购人" align="center" />
|
||||||
|
<el-table-column prop="admin_user.username" label="创建人" align="center" />
|
||||||
|
<el-table-column prop="note" label="备注" align="center" />
|
||||||
|
<el-table-column prop="created_at" label="创建时间" align="center" />
|
||||||
|
</el-table>
|
||||||
|
<div class="page-pagination">
|
||||||
|
<el-pagination
|
||||||
|
:current-page="page"
|
||||||
|
background
|
||||||
|
layout="prev, pager, next, sizes, total"
|
||||||
|
:total="total"
|
||||||
|
:page-sizes="[10, 50, 100]"
|
||||||
|
:page-size="pageSize"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange"></el-pagination>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-dialog v-model="showDialog" width="900px" title="新增" @close="resetForm">
|
||||||
|
<el-form label-position="right" label-width="110px">
|
||||||
|
<el-form-item label="采购类型:">
|
||||||
|
<el-radio-group v-model="itemInfo.type">
|
||||||
|
<el-radio label="default">默认</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="供应商:">
|
||||||
|
<el-select v-model="itemInfo.supplier_id" placeholder="请选择" clearable filterable>
|
||||||
|
<el-option v-for="it in suppliersList" :key="it.id" :label="it.name" :value="it.id" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="仓库:">
|
||||||
|
<el-select v-model="itemInfo.warehouse_id" placeholder="请选择" clearable filterable>
|
||||||
|
<el-option v-for="it in warehouseList" :key="it.id" :label="it.name" :value="it.id" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="采购人:">
|
||||||
|
<el-input v-model="itemInfo.purchasing_agent" clearable></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="入仓时间:">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="itemInfo.in_put_time"
|
||||||
|
type="datetime"
|
||||||
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
value-format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
placeholder="选择日期"
|
||||||
|
clearable>
|
||||||
|
</el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="批次号:">
|
||||||
|
<el-input v-model="itemInfo.batch_number" clearable></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="采购单:">
|
||||||
|
<el-upload
|
||||||
|
ref="uploadRef"
|
||||||
|
action="/api/upload/img"
|
||||||
|
:headers="headers"
|
||||||
|
:on-remove="handleImgRemove"
|
||||||
|
:on-error="handleUploadError"
|
||||||
|
:on-preview="handlePreview"
|
||||||
|
:on-success="handleImgSuccess"
|
||||||
|
:file-list="imgsList"
|
||||||
|
:show-file-list="true"
|
||||||
|
list-type="picture-card"
|
||||||
|
accept=".png,.jpg,.gif">
|
||||||
|
<el-icon><Plus /></el-icon>
|
||||||
|
</el-upload>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="附件:">
|
||||||
|
<el-upload
|
||||||
|
action="/api/upload/file"
|
||||||
|
:on-success="handleFileSuccess"
|
||||||
|
:on-remove="handleFileRemove"
|
||||||
|
:before-upload="handleBeforeUpload"
|
||||||
|
:on-error="handleUploadError"
|
||||||
|
:on-preview="previewFile"
|
||||||
|
:headers="headers"
|
||||||
|
:file-list="fileList"
|
||||||
|
:show-file-list="true">
|
||||||
|
<el-button type="primary">上传附件</el-button>
|
||||||
|
<template #tip>
|
||||||
|
<div class="el-upload__tip">请上传zip、pdf、excel、doc(docx)格式附件,最大限制为 4 M</div>
|
||||||
|
</template>
|
||||||
|
</el-upload>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="备注:">
|
||||||
|
<el-input v-model="itemInfo.note" type="textarea" :rows="4"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<div v-for="(item, i) in skusList" :key="i" class="skuBox">
|
||||||
|
<div class="tit">采购商品{{ i + 1 }}</div>
|
||||||
|
<el-form label-width="110px" :inline="true">
|
||||||
|
<!-- <el-form-item label="所属商品:">
|
||||||
|
<el-select v-model="item.goods_id" placeholder="请选择" clearable filterable style="width: 220px;" @change="changeGoods(item)">
|
||||||
|
<el-option v-for="it in allGoodsList" :key="it.id" :label="it.title" :value="it.id" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item> -->
|
||||||
|
<el-form-item label="商品/规格:" style="width: 100%;">
|
||||||
|
<el-select v-model="item.sku_id" placeholder="请选择" clearable filterable style="width: 680px;">
|
||||||
|
<el-option v-for="it in guigeList" :key="it.id" :label="it.goods && it.goods.title + ' - ' + it.title" :value="it.id" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="采购数量:">
|
||||||
|
<el-input-number v-model="item.num" :min="0" style="width: 270px;" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="采购成本:">
|
||||||
|
<el-input placeholder="采购成本" v-model="item.cost" clearable style="width: 270px;"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="生产日期:">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="item.production_date"
|
||||||
|
type="date"
|
||||||
|
format="YYYY-MM-DD"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
placeholder="选择日期"
|
||||||
|
style="width: 270px;"
|
||||||
|
clearable>
|
||||||
|
</el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="出厂日期:">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="item.factory_date"
|
||||||
|
type="date"
|
||||||
|
format="YYYY-MM-DD"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
placeholder="选择日期"
|
||||||
|
style="width: 270px;"
|
||||||
|
clearable>
|
||||||
|
</el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="有效期类型:">
|
||||||
|
<el-radio-group v-model="item.validity_type" style="width: 270px;">
|
||||||
|
<el-radio label="day">天</el-radio>
|
||||||
|
<el-radio label="month">月</el-radio>
|
||||||
|
<el-radio label="year">年</el-radio>
|
||||||
|
<el-radio label="none">长期</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="有效时间:">
|
||||||
|
<el-input-number v-model="item.validity" :min="0" style="width: 270px;" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="批次号:">
|
||||||
|
<el-input placeholder="批次号" v-model="item.batch_number" clearable style="width: 270px;"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label=" ">
|
||||||
|
<el-button type="danger" @click="handleSkuDelete(i)" size="small"><el-icon><Delete /></el-icon> 删除</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
<el-form label-position="right" label-width="110px">
|
||||||
|
<el-form-item label="">
|
||||||
|
<el-button type="success" @click="toAddSku()">增加采购商品</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="showDialog = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="commitOpa()" :loading="opa_loading">确定</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 预览图片 -->
|
||||||
|
<el-dialog v-model="picVisible" center width="800px">
|
||||||
|
<img :src="dialogImageUrl" style="max-width: 700px; margin: 0 auto;display: block;" />
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { onMounted, reactive, toRefs, ref, nextTick } from "vue"
|
||||||
|
import { get, post } from "@/api/request"
|
||||||
|
import { Search, Plus, Edit, ZoomIn, Delete } from '@element-plus/icons'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import { parseErrors } from 'components/common'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Search, Plus, Edit, ZoomIn, Delete
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const data = reactive({
|
||||||
|
goodsList: [],
|
||||||
|
page: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0,
|
||||||
|
loading: false,
|
||||||
|
pickerTime: [],
|
||||||
|
showDialog: false,
|
||||||
|
opa_loading: false,
|
||||||
|
headers: {
|
||||||
|
Authorization: localStorage.getItem('token'),
|
||||||
|
'Shop-Id': localStorage.getItem('shopId') || ''
|
||||||
|
},
|
||||||
|
picVisible: false,
|
||||||
|
dialogImageUrl: '',
|
||||||
|
imgsList: [],
|
||||||
|
fileList: [],
|
||||||
|
Type: {
|
||||||
|
'default': '默认'
|
||||||
|
},
|
||||||
|
warehouseList: [],
|
||||||
|
suppliersList: [],
|
||||||
|
guigeList: [],
|
||||||
|
itemInfo: {},
|
||||||
|
skusList: [],
|
||||||
|
qaFileList: [],
|
||||||
|
allGoodsList: [],
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleSearch() {
|
||||||
|
data.page = 1
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchData = () => {
|
||||||
|
data.loading = true
|
||||||
|
let params = {
|
||||||
|
page: data.page,
|
||||||
|
pageSize: data.pageSize,
|
||||||
|
service_id: data.service_id,
|
||||||
|
start_date: data.pickerTime ? data.pickerTime[0] : '',
|
||||||
|
end_date: data.pickerTime ? data.pickerTime[1] : ''
|
||||||
|
}
|
||||||
|
get(`/api/purchases`, params).then((res) => {
|
||||||
|
data.goodsList = res.data
|
||||||
|
data.total = res.meta.total
|
||||||
|
data.loading = false
|
||||||
|
}).catch(() => {
|
||||||
|
data.loading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCurrentChange(e) {
|
||||||
|
data.page = e
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSizeChange(e) {
|
||||||
|
data.page = 1
|
||||||
|
data.pageSize = e
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleAdd() {
|
||||||
|
data.imgsList = []
|
||||||
|
data.qaFileList = []
|
||||||
|
data.skusList = []
|
||||||
|
data.itemInfo = {
|
||||||
|
type: 'default'
|
||||||
|
}
|
||||||
|
data.showDialog = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function commitOpa() {
|
||||||
|
if(!data.skusList.length) {
|
||||||
|
ElMessage({ type: 'info', message: '请添加采购商品' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data.opa_loading = true
|
||||||
|
let imgs = []
|
||||||
|
data.imgsList.forEach((it) => {
|
||||||
|
imgs.push(it.url)
|
||||||
|
})
|
||||||
|
let params = {
|
||||||
|
...data.itemInfo
|
||||||
|
}
|
||||||
|
params.goodsSkus = data.skusList
|
||||||
|
params.images = imgs
|
||||||
|
params.attachments = data.qaFileList.length ? data.qaFileList[0].url : ''
|
||||||
|
post(`/api/purchases`, params).then(() => {
|
||||||
|
data.page = 1
|
||||||
|
fetchData()
|
||||||
|
ElMessage({ type: 'success', message: '新增成功' })
|
||||||
|
data.showDialog = false
|
||||||
|
data.opa_loading = false
|
||||||
|
}).catch((err) => {
|
||||||
|
data.opa_loading = false
|
||||||
|
parseErrors(err.response.data.errors)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleImgRemove(res, ress) {
|
||||||
|
data.replyImg = ress
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleUploadError(err) {
|
||||||
|
if(JSON.parse(err.message) && JSON.parse(err.message).message) {
|
||||||
|
ElMessage({ type: 'error', message: JSON.parse(err.message).message })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePreview(File) {
|
||||||
|
data.dialogImageUrl = File.url
|
||||||
|
data.picVisible = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleImgSuccess(res) {
|
||||||
|
data.imgsList.push({url: res.data.link})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleFileSuccess(res, ress) {
|
||||||
|
if (res.code === 0) {
|
||||||
|
data.qaFileList = [{ name: ress.name, url: res.data.link }]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function handleFileRemove(res, ress) {
|
||||||
|
data.qaFileList = ress
|
||||||
|
}
|
||||||
|
|
||||||
|
function previewFile(File) {
|
||||||
|
window.open(File.response.data.link)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleBeforeUpload = (file) => {
|
||||||
|
let type = file.name.indexOf('.pdf') !== -1 || file.name.indexOf('.doc') !== -1 || file.name.indexOf('.docx') !== -1 || file.name.indexOf('.zip') !== -1 || file.name.indexOf('.xlsx') !== -1
|
||||||
|
if (type) {
|
||||||
|
const isLt4M = file.size / 1024 / 1024 < 4
|
||||||
|
if (!isLt4M) {
|
||||||
|
ElMessage.error('附件上传大小不能超过 4MB!')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ElMessage.error('上传文件格式不正确')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSkuDelete(index) {
|
||||||
|
data.skusList.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const uploadRef = ref(null)
|
||||||
|
|
||||||
|
const resetForm = () => {
|
||||||
|
data.imgsList = []
|
||||||
|
nextTick(() => {
|
||||||
|
uploadRef.value.clearFiles()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function toAddSku() {
|
||||||
|
let sku = {
|
||||||
|
goods_id: '',
|
||||||
|
sku_id: '',
|
||||||
|
num: 1,
|
||||||
|
cost: '',
|
||||||
|
production_date: '',
|
||||||
|
factory_date: '',
|
||||||
|
validity_type: '',
|
||||||
|
validity: '',
|
||||||
|
batch_number: ''
|
||||||
|
}
|
||||||
|
data.skusList.push(sku)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getWarehouseList() {
|
||||||
|
get(`/api/all/warehouses`).then((res) => {
|
||||||
|
data.warehouseList = res.data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSuppliersList() {
|
||||||
|
get(`/api/suppliers`, {pageSize: 1000}).then((res) => {
|
||||||
|
data.suppliersList = res.data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSkusList() {
|
||||||
|
get(`/api/all/goods-skus`).then((res) => {
|
||||||
|
data.guigeList = res.data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAllGoodsList() {
|
||||||
|
get(`/api/all/goods`).then((res) => {
|
||||||
|
data.allGoodsList = res.data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeGoods(row) {
|
||||||
|
row.sku_id = ''
|
||||||
|
get(`/api/all/goods-skus`, {goods_id: row.goods_id}).then((res) => {
|
||||||
|
data.guigeList = res.data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchData()
|
||||||
|
getWarehouseList()
|
||||||
|
getSuppliersList()
|
||||||
|
getSkusList()
|
||||||
|
getAllGoodsList()
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
uploadRef,
|
||||||
|
...toRefs(data),
|
||||||
|
handleSearch,
|
||||||
|
handleCurrentChange,
|
||||||
|
handleSizeChange,
|
||||||
|
fetchData,
|
||||||
|
handleAdd,
|
||||||
|
commitOpa,
|
||||||
|
handleImgRemove,
|
||||||
|
handleUploadError,
|
||||||
|
handlePreview,
|
||||||
|
handleImgSuccess,
|
||||||
|
handleSkuDelete,
|
||||||
|
resetForm,
|
||||||
|
toAddSku,
|
||||||
|
handleFileSuccess,
|
||||||
|
handleFileRemove,
|
||||||
|
previewFile,
|
||||||
|
handleBeforeUpload,
|
||||||
|
getWarehouseList,
|
||||||
|
getSuppliersList,
|
||||||
|
getSkusList,
|
||||||
|
getAllGoodsList,
|
||||||
|
changeGoods
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.searchBox{
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 15px 0 0 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
.row{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 20%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
&.row1{
|
||||||
|
width: 25%;
|
||||||
|
}
|
||||||
|
.span{
|
||||||
|
display: block;
|
||||||
|
width: 80px;
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: right;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.right{
|
||||||
|
width: calc(100% - 100px);
|
||||||
|
}
|
||||||
|
.wid100{
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.opaBox{
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
.imgBox{
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
.el-image{
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.skuBox{
|
||||||
|
border: 1px solid #e5e5e5;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 15px 0;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
background-color: #f3f3f3;
|
||||||
|
.tit{
|
||||||
|
padding-left: 40px;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 15px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
12
src/views/redirect/index.vue
Normal file
12
src/views/redirect/index.vue
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
created() {
|
||||||
|
const { params, query } = this.$route
|
||||||
|
const { path } = params
|
||||||
|
this.$router.replace({ path: '/' + path, query })
|
||||||
|
},
|
||||||
|
render: function(h) {
|
||||||
|
return h() // avoid warning message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
276
src/views/shops/index.vue
Normal file
276
src/views/shops/index.vue
Normal file
@ -0,0 +1,276 @@
|
|||||||
|
<template>
|
||||||
|
<div class="pageBox">
|
||||||
|
<!-- <div class="searchBox">
|
||||||
|
<div class="row">
|
||||||
|
<span class="span">商品名称:</span>
|
||||||
|
<div class="right"><el-input v-model="goods_name" class="wid100" clearable></el-input></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<el-button type="primary" @click="handleSearch"><el-icon><Search /></el-icon> 筛选</el-button>
|
||||||
|
</div>
|
||||||
|
</div> -->
|
||||||
|
|
||||||
|
<el-card shadow="never">
|
||||||
|
<div class="opaBox">
|
||||||
|
<el-button type="primary" @click="handleAdd"><el-icon><Plus /></el-icon> 新增</el-button>
|
||||||
|
<!-- <el-button type="warning" @click="handleExport"><span class="iconfont icon-daochu"></span> 导出</el-button> -->
|
||||||
|
</div>
|
||||||
|
<el-table :data="shopsList" style="width: 100%" border v-loading="loading">
|
||||||
|
<el-table-column prop="id" label="ID" width="80" align="center" />
|
||||||
|
<el-table-column prop="name" label="店铺名称" align="center" />
|
||||||
|
<el-table-column label="平台" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<span>{{ Plat[scope.row.plat] }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="mx_supplier_name" label="供应商" align="center" />
|
||||||
|
<el-table-column label="ERP库存同步" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<span>{{ scope.row.sync_stock ? '启用' : '不启用' }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" align="center" width="150">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button type="primary" circle @click="handleEdit(scope.row)"><el-icon><Edit /></el-icon></el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div class="page-pagination">
|
||||||
|
<el-pagination
|
||||||
|
:current-page="page"
|
||||||
|
background
|
||||||
|
layout="prev, pager, next, sizes, total"
|
||||||
|
:total="total"
|
||||||
|
:page-sizes="[10, 50, 100]"
|
||||||
|
:page-size="pageSize"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange"></el-pagination>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-dialog v-model="showDialog" width="900px" :title="opaType == 'add' ? '新增' : '编辑' ">
|
||||||
|
<el-form label-position="right" label-width="110px">
|
||||||
|
<el-form-item label="店铺名称:">
|
||||||
|
<el-input v-model="itemInfo.name" clearable></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="平台:">
|
||||||
|
<el-radio-group v-model="itemInfo.plat">
|
||||||
|
<el-radio label="MX">妙选</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="供应商:">
|
||||||
|
<el-select v-model="itemInfo.mx_supplier_id" placeholder="请选择" clearable filterable>
|
||||||
|
<el-option v-for="it in suppliersList" :key="it.id" :label="it.name" :value="it.id" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="是否同步库存:">
|
||||||
|
<el-radio-group v-model="itemInfo.sync_stock">
|
||||||
|
<el-radio :label="1">是</el-radio>
|
||||||
|
<el-radio :label="0">否</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="showDialog = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="commitOpa()" :loading="opa_loading">确定</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { onMounted, reactive, toRefs, ref, nextTick } from "vue"
|
||||||
|
import { get, post } from "@/api/request"
|
||||||
|
import { Search, Plus, Edit, ZoomIn, Delete } from '@element-plus/icons'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import { parseErrors } from 'components/common'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Search, Plus, Edit, ZoomIn, Delete
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const data = reactive({
|
||||||
|
shopsList: [],
|
||||||
|
page: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0,
|
||||||
|
loading: false,
|
||||||
|
pickerTime: [],
|
||||||
|
opaType: '',
|
||||||
|
showDialog: false,
|
||||||
|
opa_loading: false,
|
||||||
|
Plat: {
|
||||||
|
'MX': '妙选'
|
||||||
|
},
|
||||||
|
suppliersList: [],
|
||||||
|
itemInfo: {}
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleSearch() {
|
||||||
|
data.page = 1
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchData = () => {
|
||||||
|
data.loading = true
|
||||||
|
let params = {
|
||||||
|
page: data.page,
|
||||||
|
pageSize: data.pageSize,
|
||||||
|
service_id: data.service_id,
|
||||||
|
start_date: data.pickerTime ? data.pickerTime[0] : '',
|
||||||
|
end_date: data.pickerTime ? data.pickerTime[1] : ''
|
||||||
|
}
|
||||||
|
get(`/api/shops`, params).then((res) => {
|
||||||
|
data.shopsList = res.data
|
||||||
|
data.total = res.meta.total
|
||||||
|
data.loading = false
|
||||||
|
}).catch(() => {
|
||||||
|
data.loading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCurrentChange(e) {
|
||||||
|
data.page = e
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSizeChange(e) {
|
||||||
|
data.page = 1
|
||||||
|
data.pageSize = e
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleAdd() {
|
||||||
|
data.opaType = 'add'
|
||||||
|
data.itemInfo = {
|
||||||
|
plat: 'MX',
|
||||||
|
sync_stock: 1
|
||||||
|
}
|
||||||
|
data.showDialog = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleEdit(item) {
|
||||||
|
data.opaType = 'edit'
|
||||||
|
data.itemInfo = JSON.parse(JSON.stringify(item))
|
||||||
|
data.showDialog = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function commitOpa() {
|
||||||
|
data.opa_loading = true
|
||||||
|
let params = {
|
||||||
|
...data.itemInfo
|
||||||
|
}
|
||||||
|
if(data.opaType == 'add') {
|
||||||
|
post(`/api/shops`, params).then(() => {
|
||||||
|
data.page = 1
|
||||||
|
fetchData()
|
||||||
|
ElMessage({ type: 'success', message: '新增成功' })
|
||||||
|
data.showDialog = false
|
||||||
|
data.opa_loading = false
|
||||||
|
}).catch((err) => {
|
||||||
|
data.opa_loading = false
|
||||||
|
parseErrors(err.response.data.errors)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
post(`/api/shops/${data.itemInfo.id}`, params, 'PUT').then(() => {
|
||||||
|
fetchData()
|
||||||
|
ElMessage({ type: 'success', message: '编辑成功' })
|
||||||
|
data.showDialog = false
|
||||||
|
data.opa_loading = false
|
||||||
|
}).catch((err) => {
|
||||||
|
data.opa_loading = false
|
||||||
|
parseErrors(err.response.data.errors)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSuppliersList() {
|
||||||
|
get(`/api/mxSuppliers`, {pageSize: 1000}).then((res) => {
|
||||||
|
data.suppliersList = res.data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchData()
|
||||||
|
getSuppliersList()
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toRefs(data),
|
||||||
|
handleSearch,
|
||||||
|
handleCurrentChange,
|
||||||
|
handleSizeChange,
|
||||||
|
fetchData,
|
||||||
|
handleAdd,
|
||||||
|
handleEdit,
|
||||||
|
commitOpa,
|
||||||
|
getSuppliersList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.searchBox{
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 15px 0 0 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
.row{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 20%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
&.row1{
|
||||||
|
width: 25%;
|
||||||
|
}
|
||||||
|
.span{
|
||||||
|
display: block;
|
||||||
|
width: 80px;
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: right;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.right{
|
||||||
|
width: calc(100% - 100px);
|
||||||
|
}
|
||||||
|
.wid100{
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.opaBox{
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
.imgBox{
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
.el-image{
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.skuBox{
|
||||||
|
border: 1px solid #e5e5e5;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 15px 0;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
background-color: #f3f3f3;
|
||||||
|
.tit{
|
||||||
|
padding-left: 40px;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 15px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
639
src/views/statistics/index.vue
Normal file
639
src/views/statistics/index.vue
Normal file
@ -0,0 +1,639 @@
|
|||||||
|
<template>
|
||||||
|
<div class="pageBox">
|
||||||
|
<div class="searchBox">
|
||||||
|
<div class="row row1">
|
||||||
|
<span class="span">商品名称:</span>
|
||||||
|
<div class="right"><el-input v-model="filter.title" class="wid100" clearable></el-input></div>
|
||||||
|
</div>
|
||||||
|
<div class="row row1">
|
||||||
|
<span class="span">规格编码:</span>
|
||||||
|
<div class="right"><el-input v-model="filter.sku_code" class="wid100" clearable></el-input></div>
|
||||||
|
</div>
|
||||||
|
<div class="row row1">
|
||||||
|
<span class="span">商品品牌:</span>
|
||||||
|
<div class="right">
|
||||||
|
<el-select v-model="filter.brand_id" placeholder="请选择" clearable class="wid100">
|
||||||
|
<el-option v-for="it in brandList" :key="it.id" :label="it.name" :value="it.id" />
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row row1">
|
||||||
|
<span class="span">赠品:</span>
|
||||||
|
<div class="right">
|
||||||
|
<el-select v-model="filter.gift" placeholder="请选择" :clearable="false" class="wid100">
|
||||||
|
<el-option label="全部" value="all" />
|
||||||
|
<el-option label="否" :value="0" />
|
||||||
|
<el-option label="是" :value="1" />
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<span class="span">时间区间:</span>
|
||||||
|
<div class="right">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="rangeTime"
|
||||||
|
type="daterange"
|
||||||
|
range-separator="至"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
format="YYYY-MM-DD"
|
||||||
|
style="width: 250px;">
|
||||||
|
</el-date-picker>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<span class="span"></span>
|
||||||
|
<el-button type="primary" @click="handleSearch"><el-icon><Search /></el-icon> 筛选</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-card shadow="never">
|
||||||
|
<el-table :data="statisticsList" style="width: 100%" border v-loading="loading" @sort-change="sortChange"
|
||||||
|
:default-sort="{
|
||||||
|
prop: 'number,order_num,seven_day_avg_number,stock_wait,three_day_avg_number,three_day_stock_wait,thirty_day_number,actual_inventory,total_profit',
|
||||||
|
}">
|
||||||
|
<el-table-column prop="date" label="日期" align="center" />
|
||||||
|
<el-table-column prop="type" label="商品信息" width="220">
|
||||||
|
<template #default="scope">
|
||||||
|
<div class="goodInfo" v-if="scope.row.goods_sku">
|
||||||
|
<div class="imgBox" v-if="scope.row.goods_sku.goods && scope.row.goods_sku.goods.images">
|
||||||
|
<el-image v-for="(it, i) in scope.row.goods_sku.goods.images" :key="i" :z-index="9999"
|
||||||
|
:src="it" :hide-on-click-modal="true" :preview-src-list="[scope.row.goods_sku.goods.images]"
|
||||||
|
fit="cover" :preview-teleported="true" />
|
||||||
|
</div>
|
||||||
|
<div class="tit">{{ scope.row.goods_sku.goods.title }}({{ scope.row.goods_sku.title }})</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="number" label="销量" align="center" sortable="custom" />
|
||||||
|
<el-table-column prop="order_num" label="单数" align="center" sortable="custom" />
|
||||||
|
<el-table-column prop="seven_day_avg_number" label="7天日销" align="center" sortable="custom" />
|
||||||
|
<el-table-column prop="stock_wait" label="7天周转天数" align="center" sortable="custom" />
|
||||||
|
<el-table-column prop="three_day_avg_number" label="3天日销" align="center" sortable="custom" />
|
||||||
|
<el-table-column prop="three_day_stock_wait" label="3天周转天数" align="center" sortable="custom" />
|
||||||
|
<el-table-column prop="thirty_day_number" label="近30天销量" align="center" sortable="custom" />
|
||||||
|
<el-table-column prop="actual_inventory" label="总库存" align="center" sortable="custom">
|
||||||
|
<template #header>
|
||||||
|
<span style="margin-right: 5px;">总库存</span>
|
||||||
|
<span>
|
||||||
|
<el-tooltip placement="top" :hide-after="0" :show-after="200">
|
||||||
|
<template #content>总库存包含未锁定和锁定库存,括号里为锁定库存</template>
|
||||||
|
<el-icon size="18"><QuestionFilled /></el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<div v-if="scope.row.goods_sku">{{ scope.row.goods_sku.actual_inventory }}({{ scope.row.goods_sku.lock_in_stock }})</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="refund_amount" label="退款金额" align="center" />
|
||||||
|
<el-table-column prop="red_refund_amount" label="红包退款有责金额" align="center" />
|
||||||
|
<el-table-column prop="total_profit" label="总利润" align="center" />
|
||||||
|
<el-table-column label="数据" align="center" width="180">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button type="primary" circle @click="handleAnalysis(scope.row.sku_code)" title="店铺数据"><el-icon><Shop /></el-icon></el-button>
|
||||||
|
<el-button type="primary" circle :loading="scope.row.loading" @click="trendCharts(scope.row)" title="趋势图"><el-icon><TrendCharts /></el-icon></el-button>
|
||||||
|
<el-button type="primary" circle @click="orderCharts(scope.row)" title="订单商品销量趋势"><el-icon><List /></el-icon></el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div class="page-pagination">
|
||||||
|
<el-pagination
|
||||||
|
:current-page="page"
|
||||||
|
background
|
||||||
|
layout="prev, pager, next, sizes, total"
|
||||||
|
:total="total"
|
||||||
|
:page-sizes="[10, 50, 100]"
|
||||||
|
:page-size="pageSize"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange"></el-pagination>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-dialog v-model="showDialog" width="1000px" title="店铺数据">
|
||||||
|
<div>
|
||||||
|
<div style="margin-bottom: 15px;">
|
||||||
|
<span>时间:</span>
|
||||||
|
<el-date-picker
|
||||||
|
v-model="pickTime"
|
||||||
|
type="daterange"
|
||||||
|
range-separator="至"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
format="YYYY-MM-DD"
|
||||||
|
style="width: 250px;"
|
||||||
|
:clearable="false"
|
||||||
|
@change="getDialogList()">
|
||||||
|
</el-date-picker>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tabBox">
|
||||||
|
<el-table :data="dialogList" style="width: 100%" border v-loading="opa_loading">
|
||||||
|
<el-table-column label="排名" width="70" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<span>{{ scope.$index + 1 }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="mx_shop_name" label="店铺" align="center" />
|
||||||
|
<el-table-column prop="date" label="日期" align="center" />
|
||||||
|
<el-table-column prop="number" label="销量" align="center" />
|
||||||
|
<el-table-column prop="order_num" label="单数" align="center" />
|
||||||
|
<el-table-column prop="avg_number" label="日销量" align="center" />
|
||||||
|
<el-table-column prop="total_price" label="支付金额" align="center" />
|
||||||
|
<el-table-column prop="refund_amount" label="退款金额" align="center" />
|
||||||
|
<el-table-column prop="total_profit" label="利润" align="center" />
|
||||||
|
<el-table-column label="趋势图" align="center" width="80">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button type="primary" circle @click="getDataLine(scope.row)" :loading="scope.row.loading"><el-icon><TrendCharts /></el-icon></el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<el-dialog v-model="showChart" width="900px" title="趋势图分析">
|
||||||
|
<el-form label-width="110px">
|
||||||
|
<el-form-item label="时间:">
|
||||||
|
<div>
|
||||||
|
<el-date-picker
|
||||||
|
v-model="chartTime"
|
||||||
|
type="daterange"
|
||||||
|
range-separator="至"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
format="YYYY-MM-DD"
|
||||||
|
style="width: 250px;"
|
||||||
|
:clearable="false"
|
||||||
|
@change="changeTime0()">
|
||||||
|
</el-date-picker>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div id="lineChart" style="width: 100%;height:500px;" v-loading="loading1"></div>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<el-dialog v-model="showTrend" width="900px" title="趋势图分析">
|
||||||
|
<el-form label-width="110px" :inline="true">
|
||||||
|
<el-form-item label="时间:">
|
||||||
|
<div>
|
||||||
|
<el-date-picker
|
||||||
|
v-model="trendTime"
|
||||||
|
type="daterange"
|
||||||
|
range-separator="至"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
format="YYYY-MM-DD"
|
||||||
|
style="width: 250px;"
|
||||||
|
:clearable="false"
|
||||||
|
@change="changeTime()">
|
||||||
|
</el-date-picker>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="总计:"><span style="color: #f00;">{{ sumDaily }}</span></el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div id="trendChart" style="width: 100%;height:500px;" v-loading="loading1"></div>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<el-dialog v-model="showOrderTrend" width="900px" title="订单商品销量趋势">
|
||||||
|
<el-form label-width="80px" :inline="true">
|
||||||
|
<el-form-item label="店铺:">
|
||||||
|
<el-select v-model="from_shop_ids" @change="changeOrderTime()" placeholder="请选择" clearable filterable multiple collapse-tags style="width: 200px;">
|
||||||
|
<el-option v-for="it in shopsList" :key="it.id" :label="it.name" :value="it.id" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="时间:">
|
||||||
|
<div>
|
||||||
|
<el-date-picker
|
||||||
|
v-model="orderTrendTime"
|
||||||
|
type="datetimerange"
|
||||||
|
range-separator="至"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
value-format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
style="width: 340px;"
|
||||||
|
:clearable="false"
|
||||||
|
@change="changeOrderTime()">
|
||||||
|
</el-date-picker>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="粒度:">
|
||||||
|
<el-radio-group v-model="grain_size" size="mini" @change="changeOrderTime()">
|
||||||
|
<el-radio-button label="60分钟" :value="60" />
|
||||||
|
<el-radio-button label="30分钟" :value="30" />
|
||||||
|
<el-radio-button label="15分钟" :value="15" />
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div id="orderChart" style="width: 100%;height:500px;" v-loading="loading1"></div>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { onMounted, reactive, toRefs } from "vue"
|
||||||
|
import { get } from "@/api/request"
|
||||||
|
import { Search, Shop, TrendCharts, QuestionFilled, List } from '@element-plus/icons'
|
||||||
|
import * as echarts from 'echarts'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Search, Shop, TrendCharts, QuestionFilled, List
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const data = reactive({
|
||||||
|
filter: {
|
||||||
|
title: '',
|
||||||
|
gift: 0
|
||||||
|
},
|
||||||
|
rangeTime: [],
|
||||||
|
statisticsList: [],
|
||||||
|
page: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0,
|
||||||
|
loading: false,
|
||||||
|
opaType: '',
|
||||||
|
showDialog: false,
|
||||||
|
opa_loading: false,
|
||||||
|
itemId: 0,
|
||||||
|
from_shop_id: 0,
|
||||||
|
dialogList: [],
|
||||||
|
pickTime: [],
|
||||||
|
loading1: false,
|
||||||
|
dataLineX: [],
|
||||||
|
dataLineY: [],
|
||||||
|
showChart: false,
|
||||||
|
brandList: [],
|
||||||
|
ascOrDesc: 'desc',
|
||||||
|
sort: 'number',
|
||||||
|
showTrend: false,
|
||||||
|
sumDaily: 0,
|
||||||
|
trendTime: [],
|
||||||
|
chartTime: [],
|
||||||
|
showOrderTrend: false,
|
||||||
|
orderTrendTime: [],
|
||||||
|
from_shop_ids: [],
|
||||||
|
shopsList: [],
|
||||||
|
grain_size: 60,
|
||||||
|
dataLineY2: []
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleSearch() {
|
||||||
|
data.page = 1
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
function getShopsList() {
|
||||||
|
get(`/api/mxShops`).then((res) => {
|
||||||
|
data.shopsList = res.data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchData = () => {
|
||||||
|
data.loading = true
|
||||||
|
let params = {
|
||||||
|
...data.filter,
|
||||||
|
page: data.page,
|
||||||
|
pageSize: data.pageSize,
|
||||||
|
start_date: data.rangeTime ? data.rangeTime[0] : '',
|
||||||
|
end_date: data.rangeTime ? data.rangeTime[1] : '',
|
||||||
|
direction: data.ascOrDesc,
|
||||||
|
order_by_column: data.sort
|
||||||
|
}
|
||||||
|
params.brand_id = data.filter.brand_id || 0
|
||||||
|
get(`/api/orderItemDailyReport`, params).then((res) => {
|
||||||
|
data.statisticsList = res.data
|
||||||
|
data.total = res.meta.total
|
||||||
|
data.loading = false
|
||||||
|
}).catch(() => {
|
||||||
|
data.loading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCurrentChange(e) {
|
||||||
|
data.page = e
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSizeChange(e) {
|
||||||
|
data.page = 1
|
||||||
|
data.pageSize = e
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleAnalysis(sku_code) {
|
||||||
|
let end = dayjs().format('YYYY-MM-DD')
|
||||||
|
let start = dayjs().subtract(30, 'day').format('YYYY-MM-DD')
|
||||||
|
data.pickTime = [end, end]
|
||||||
|
data.itemId = sku_code
|
||||||
|
getDialogList()
|
||||||
|
data.showDialog = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const getDialogList = () => {
|
||||||
|
data.opa_loading = true
|
||||||
|
let params = {
|
||||||
|
sku_code: data.itemId,
|
||||||
|
start_date: data.pickTime ? data.pickTime[0] : '',
|
||||||
|
end_date: data.pickTime ? data.pickTime[1] : ''
|
||||||
|
}
|
||||||
|
get(`/api/mxShopDailyReport`, params).then((res) => {
|
||||||
|
data.dialogList = res.data
|
||||||
|
data.opa_loading = false
|
||||||
|
}).catch(() => {
|
||||||
|
data.opa_loading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const getDataLine = (row) => {
|
||||||
|
row.loading = true
|
||||||
|
data.loading1 = true
|
||||||
|
let end = dayjs().format('YYYY-MM-DD')
|
||||||
|
let start = dayjs().subtract(30, 'day').format('YYYY-MM-DD')
|
||||||
|
data.chartTime = [start, end]
|
||||||
|
data.from_shop_id = row.from_shop_id
|
||||||
|
let params = {
|
||||||
|
from_shop_id: row.from_shop_id,
|
||||||
|
sku_code: data.itemId,
|
||||||
|
start_date: data.chartTime ? data.chartTime[0] : '',
|
||||||
|
end_date: data.chartTime ? data.chartTime[1] : ''
|
||||||
|
}
|
||||||
|
get(`/api/mxShopTendency`, params).then((res) => {
|
||||||
|
data.dataLineX = []
|
||||||
|
data.dataLineY = []
|
||||||
|
res.data.forEach((item) => {
|
||||||
|
data.dataLineX.push(item.date)
|
||||||
|
data.dataLineY.push(item.number)
|
||||||
|
})
|
||||||
|
data.showChart = true
|
||||||
|
setTimeout(() => {
|
||||||
|
getTrendChart('lineChart')
|
||||||
|
data.loading1 = false
|
||||||
|
}, 500)
|
||||||
|
row.loading = false
|
||||||
|
}).catch(() => {
|
||||||
|
row.loading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeTime0() {
|
||||||
|
data.loading1 = true
|
||||||
|
let params = {
|
||||||
|
from_shop_id: data.from_shop_id,
|
||||||
|
sku_code: data.itemId,
|
||||||
|
start_date: data.chartTime ? data.chartTime[0] : '',
|
||||||
|
end_date: data.chartTime ? data.chartTime[1] : ''
|
||||||
|
}
|
||||||
|
get(`/api/mxShopTendency`, params).then((res) => {
|
||||||
|
data.dataLineX = []
|
||||||
|
data.dataLineY = []
|
||||||
|
res.data.forEach((item) => {
|
||||||
|
data.dataLineX.push(item.date)
|
||||||
|
data.dataLineY.push(item.number)
|
||||||
|
})
|
||||||
|
setTimeout(() => {
|
||||||
|
getTrendChart('lineChart')
|
||||||
|
data.loading1 = false
|
||||||
|
}, 500)
|
||||||
|
}).catch(() => {
|
||||||
|
data.loading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTrendChart(id, val = 0) {
|
||||||
|
let myChart = echarts.init(document.getElementById(id))
|
||||||
|
let option = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis'
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
data: ['销量']
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: data.dataLineX
|
||||||
|
},
|
||||||
|
yAxis: {},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '销量',
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
data: data.dataLineY
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
if(val == 2) {
|
||||||
|
option.legend.data.push('累计销量')
|
||||||
|
option.series.push({
|
||||||
|
name: '累计销量',
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
data: data.dataLineY2
|
||||||
|
})
|
||||||
|
}
|
||||||
|
myChart.setOption(option)
|
||||||
|
}
|
||||||
|
|
||||||
|
function trendCharts(row) {
|
||||||
|
data.itemId = row.sku_code
|
||||||
|
row.loading = true
|
||||||
|
data.loading1 = true
|
||||||
|
let end = dayjs().format('YYYY-MM-DD')
|
||||||
|
let start = dayjs().subtract(30, 'day').format('YYYY-MM-DD')
|
||||||
|
data.trendTime = [start, end]
|
||||||
|
let params = {
|
||||||
|
sku_code: row.sku_code,
|
||||||
|
start_date: data.trendTime ? data.trendTime[0] : '',
|
||||||
|
end_date: data.trendTime ? data.trendTime[1] : ''
|
||||||
|
}
|
||||||
|
get(`/api/orderItemDailyTendency`, params).then((res) => {
|
||||||
|
data.dataLineX = []
|
||||||
|
data.dataLineY = []
|
||||||
|
res.data.forEach((item) => {
|
||||||
|
data.dataLineX.push(item.date)
|
||||||
|
data.dataLineY.push(item.number)
|
||||||
|
})
|
||||||
|
data.sumDaily = res.sum
|
||||||
|
data.showTrend = true
|
||||||
|
setTimeout(() => {
|
||||||
|
getTrendChart('trendChart')
|
||||||
|
data.loading1 = false
|
||||||
|
}, 500)
|
||||||
|
row.loading = false
|
||||||
|
}).catch(() => {
|
||||||
|
row.loading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeTime() {
|
||||||
|
data.loading1 = true
|
||||||
|
let params = {
|
||||||
|
sku_code: data.itemId,
|
||||||
|
start_date: data.trendTime ? data.trendTime[0] : '',
|
||||||
|
end_date: data.trendTime ? data.trendTime[1] : ''
|
||||||
|
}
|
||||||
|
get(`/api/orderItemDailyTendency`, params).then((res) => {
|
||||||
|
data.dataLineX = []
|
||||||
|
data.dataLineY = []
|
||||||
|
res.data.forEach((item) => {
|
||||||
|
data.dataLineX.push(item.date)
|
||||||
|
data.dataLineY.push(item.number)
|
||||||
|
})
|
||||||
|
data.sumDaily = res.sum
|
||||||
|
setTimeout(() => {
|
||||||
|
getTrendChart('trendChart')
|
||||||
|
data.loading1 = false
|
||||||
|
}, 500)
|
||||||
|
data.loading1 = false
|
||||||
|
}).catch(() => {
|
||||||
|
data.loading1 = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBrandList() {
|
||||||
|
get(`/api/all/brands`).then((res) => {
|
||||||
|
data.brandList = res.data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortChange({ prop, order }) {
|
||||||
|
console.log(prop, order)
|
||||||
|
let arr = prop.split('.')
|
||||||
|
let length = arr.length
|
||||||
|
data.ascOrDesc = order == 'ascending' ? 'asc' : 'desc'
|
||||||
|
data.sort = arr[length - 1]
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
function orderCharts(row) {
|
||||||
|
data.from_shop_ids = []
|
||||||
|
data.itemId = row.sku_code
|
||||||
|
data.grain_size = 60
|
||||||
|
let end = dayjs().format('YYYY-MM-DD HH:mm:ss')
|
||||||
|
let start = dayjs().format('YYYY-MM-DD') + ' 00:00:00'
|
||||||
|
data.orderTrendTime = [start, end]
|
||||||
|
data.showOrderTrend = true
|
||||||
|
getOrderCharts()
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeOrderTime() {
|
||||||
|
getOrderCharts()
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOrderCharts() {
|
||||||
|
data.loading1 = true
|
||||||
|
let params = {
|
||||||
|
sku_code: data.itemId,
|
||||||
|
from_shop_ids: data.from_shop_ids,
|
||||||
|
start_time: data.orderTrendTime ? data.orderTrendTime[0] : '',
|
||||||
|
end_time: data.orderTrendTime ? data.orderTrendTime[1] : '',
|
||||||
|
minute: data.grain_size
|
||||||
|
}
|
||||||
|
get(`/api/orderItemTendency`, params).then((res) => {
|
||||||
|
data.dataLineX = []
|
||||||
|
data.dataLineY = []
|
||||||
|
data.dataLineY2 = []
|
||||||
|
res.data.forEach((item) => {
|
||||||
|
data.dataLineX.push(item.time_period)
|
||||||
|
data.dataLineY.push(item.total_number)
|
||||||
|
data.dataLineY2.push(item.total)
|
||||||
|
})
|
||||||
|
setTimeout(() => {
|
||||||
|
getTrendChart('orderChart', 2)
|
||||||
|
data.loading1 = false
|
||||||
|
}, 500)
|
||||||
|
data.loading1 = false
|
||||||
|
}).catch(() => {
|
||||||
|
data.loading1 = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchData()
|
||||||
|
getBrandList()
|
||||||
|
getShopsList()
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toRefs(data),
|
||||||
|
handleSearch,
|
||||||
|
handleCurrentChange,
|
||||||
|
handleSizeChange,
|
||||||
|
fetchData,
|
||||||
|
handleAnalysis,
|
||||||
|
getDataLine,
|
||||||
|
getBrandList,
|
||||||
|
getDialogList,
|
||||||
|
sortChange,
|
||||||
|
trendCharts,
|
||||||
|
changeTime,
|
||||||
|
changeTime0,
|
||||||
|
orderCharts,
|
||||||
|
getOrderCharts,
|
||||||
|
changeOrderTime,
|
||||||
|
getShopsList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.searchBox{
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 15px 0 0 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
.row{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: auto;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
margin-right: 15px;
|
||||||
|
&.row1{
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
.span{
|
||||||
|
display: block;
|
||||||
|
width: 80px;
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: right;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.right{
|
||||||
|
width: calc(100% - 80px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.imgBox{
|
||||||
|
.el-image{
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-right: 10px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.skuBox{
|
||||||
|
border: 1px solid #e5e5e5;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 15px 0;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
background-color: #f3f3f3;
|
||||||
|
.tit{
|
||||||
|
padding-left: 40px;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 15px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
272
src/views/supplier/index.vue
Normal file
272
src/views/supplier/index.vue
Normal file
@ -0,0 +1,272 @@
|
|||||||
|
<template>
|
||||||
|
<div class="pageBox">
|
||||||
|
<!-- <div class="searchBox">
|
||||||
|
<div class="row">
|
||||||
|
<span class="span">商品名称:</span>
|
||||||
|
<div class="right"><el-input v-model="goods_name" class="wid100" clearable></el-input></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<el-button type="primary" @click="handleSearch"><el-icon><Search /></el-icon> 筛选</el-button>
|
||||||
|
</div>
|
||||||
|
</div> -->
|
||||||
|
|
||||||
|
<el-card shadow="never">
|
||||||
|
<div class="opaBox">
|
||||||
|
<el-button type="primary" @click="handleAdd"><el-icon><Plus /></el-icon> 新增</el-button>
|
||||||
|
<!-- <el-button type="warning" @click="handleExport"><span class="iconfont icon-daochu"></span> 导出</el-button> -->
|
||||||
|
</div>
|
||||||
|
<el-table :data="goodsList" style="width: 100%" border v-loading="loading">
|
||||||
|
<el-table-column prop="id" label="ID" width="80" align="center" />
|
||||||
|
<el-table-column prop="name" label="供应商名称" align="center" />
|
||||||
|
<el-table-column prop="type" label="供应商类型" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<span>{{ Type[scope.row.type] }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="状态" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<span>{{ scope.row.status ? '启用' : '不启用' }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="admin_user.username" label="添加人" align="center" />
|
||||||
|
<el-table-column prop="sort" label="排序" align="center" />
|
||||||
|
<el-table-column label="操作" align="center" width="150">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button type="primary" circle @click="handleEdit(scope.row)"><el-icon><Edit /></el-icon></el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div class="page-pagination">
|
||||||
|
<el-pagination
|
||||||
|
:current-page="page"
|
||||||
|
background
|
||||||
|
layout="prev, pager, next, sizes, total"
|
||||||
|
:total="total"
|
||||||
|
:page-sizes="[10, 50, 100]"
|
||||||
|
:page-size="pageSize"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange"></el-pagination>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-dialog v-model="showDialog" width="900px" :title="opaType == 'add' ? '新增' : '编辑' ">
|
||||||
|
<el-form label-position="right" label-width="110px">
|
||||||
|
<el-form-item label="供应商名称:">
|
||||||
|
<el-input v-model="itemInfo.name" clearable></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="供应商类型:">
|
||||||
|
<el-radio-group v-model="itemInfo.type">
|
||||||
|
<el-radio label="default">默认</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="状态:">
|
||||||
|
<el-radio-group v-model="itemInfo.status">
|
||||||
|
<el-radio :label="1">启用</el-radio>
|
||||||
|
<el-radio :label="0">不启用</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="备注:">
|
||||||
|
<el-input v-model="itemInfo.note" type="textarea" :rows="4"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="排序:">
|
||||||
|
<el-input-number v-model="itemInfo.sort" :min="0" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="showDialog = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="commitOpa()" :loading="opa_loading">确定</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { onMounted, reactive, toRefs, ref, nextTick } from "vue"
|
||||||
|
import { get, post } from "@/api/request"
|
||||||
|
import { Search, Plus, Edit, ZoomIn, Delete } from '@element-plus/icons'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import { parseErrors } from 'components/common'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Search, Plus, Edit, ZoomIn, Delete
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const data = reactive({
|
||||||
|
goodsList: [],
|
||||||
|
page: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0,
|
||||||
|
loading: false,
|
||||||
|
pickerTime: [],
|
||||||
|
opaType: '',
|
||||||
|
showDialog: false,
|
||||||
|
opa_loading: false,
|
||||||
|
Type: {
|
||||||
|
'default': '默认'
|
||||||
|
},
|
||||||
|
warehouseList: [],
|
||||||
|
itemInfo: {}
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleSearch() {
|
||||||
|
data.page = 1
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchData = () => {
|
||||||
|
data.loading = true
|
||||||
|
let params = {
|
||||||
|
page: data.page,
|
||||||
|
pageSize: data.pageSize,
|
||||||
|
service_id: data.service_id,
|
||||||
|
start_date: data.pickerTime ? data.pickerTime[0] : '',
|
||||||
|
end_date: data.pickerTime ? data.pickerTime[1] : ''
|
||||||
|
}
|
||||||
|
get(`/api/suppliers`, params).then((res) => {
|
||||||
|
data.goodsList = res.data
|
||||||
|
data.total = res.meta.total
|
||||||
|
data.loading = false
|
||||||
|
}).catch(() => {
|
||||||
|
data.loading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCurrentChange(e) {
|
||||||
|
data.page = e
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSizeChange(e) {
|
||||||
|
data.page = 1
|
||||||
|
data.pageSize = e
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleAdd() {
|
||||||
|
data.opaType = 'add'
|
||||||
|
data.itemInfo = {
|
||||||
|
type: 'default',
|
||||||
|
sort: 0,
|
||||||
|
status: 1
|
||||||
|
}
|
||||||
|
data.showDialog = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleEdit(item) {
|
||||||
|
data.opaType = 'edit'
|
||||||
|
data.itemInfo = JSON.parse(JSON.stringify(item))
|
||||||
|
data.showDialog = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function commitOpa() {
|
||||||
|
data.opa_loading = true
|
||||||
|
let params = {
|
||||||
|
...data.itemInfo
|
||||||
|
}
|
||||||
|
if(data.opaType == 'add') {
|
||||||
|
post(`/api/suppliers`, params).then(() => {
|
||||||
|
data.page = 1
|
||||||
|
fetchData()
|
||||||
|
ElMessage({ type: 'success', message: '新增成功' })
|
||||||
|
data.showDialog = false
|
||||||
|
data.opa_loading = false
|
||||||
|
}).catch((err) => {
|
||||||
|
data.opa_loading = false
|
||||||
|
parseErrors(err.response.data.errors)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
post(`/api/suppliers/${data.itemInfo.id}`, params, 'PUT').then(() => {
|
||||||
|
fetchData()
|
||||||
|
ElMessage({ type: 'success', message: '编辑成功' })
|
||||||
|
data.showDialog = false
|
||||||
|
data.opa_loading = false
|
||||||
|
}).catch((err) => {
|
||||||
|
data.opa_loading = false
|
||||||
|
parseErrors(err.response.data.errors)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchData()
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toRefs(data),
|
||||||
|
handleSearch,
|
||||||
|
handleCurrentChange,
|
||||||
|
handleSizeChange,
|
||||||
|
fetchData,
|
||||||
|
handleAdd,
|
||||||
|
handleEdit,
|
||||||
|
commitOpa,
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.searchBox{
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 15px 0 0 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
.row{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 20%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
&.row1{
|
||||||
|
width: 25%;
|
||||||
|
}
|
||||||
|
.span{
|
||||||
|
display: block;
|
||||||
|
width: 80px;
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: right;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.right{
|
||||||
|
width: calc(100% - 100px);
|
||||||
|
}
|
||||||
|
.wid100{
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.opaBox{
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
.imgBox{
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
.el-image{
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.skuBox{
|
||||||
|
border: 1px solid #e5e5e5;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 15px 0;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
background-color: #f3f3f3;
|
||||||
|
.tit{
|
||||||
|
padding-left: 40px;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 15px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
274
src/views/warehouse/index.vue
Normal file
274
src/views/warehouse/index.vue
Normal file
@ -0,0 +1,274 @@
|
|||||||
|
<template>
|
||||||
|
<div class="pageBox">
|
||||||
|
<!-- <div class="searchBox">
|
||||||
|
<div class="row">
|
||||||
|
<span class="span">商品名称:</span>
|
||||||
|
<div class="right"><el-input v-model="goods_name" class="wid100" clearable></el-input></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<el-button type="primary" @click="handleSearch"><el-icon><Search /></el-icon> 筛选</el-button>
|
||||||
|
</div>
|
||||||
|
</div> -->
|
||||||
|
|
||||||
|
<el-card shadow="never">
|
||||||
|
<div class="opaBox">
|
||||||
|
<el-button type="primary" @click="handleAdd"><el-icon><Plus /></el-icon> 新增</el-button>
|
||||||
|
<!-- <el-button type="warning" @click="handleExport"><span class="iconfont icon-daochu"></span> 导出</el-button> -->
|
||||||
|
</div>
|
||||||
|
<el-table :data="goodsList" style="width: 100%" border v-loading="loading">
|
||||||
|
<el-table-column prop="id" label="ID" width="80" align="center" />
|
||||||
|
<el-table-column prop="name" label="仓库名称" align="center" />
|
||||||
|
<el-table-column prop="type" label="仓库类型" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<span>{{ Type[scope.row.type] }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="仓库状态" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<span>{{ scope.row.status ? '启用' : '不启用' }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="admin_user.username" label="添加人" align="center" />
|
||||||
|
<el-table-column prop="note" label="备注" align="center" />
|
||||||
|
<el-table-column prop="sort" label="排序" align="center" />
|
||||||
|
<el-table-column label="操作" align="center" width="150">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button type="primary" circle @click="handleEdit(scope.row)"><el-icon><Edit /></el-icon></el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div class="page-pagination">
|
||||||
|
<el-pagination
|
||||||
|
:current-page="page"
|
||||||
|
background
|
||||||
|
layout="prev, pager, next, sizes, total"
|
||||||
|
:total="total"
|
||||||
|
:page-sizes="[10, 50, 100]"
|
||||||
|
:page-size="pageSize"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange"></el-pagination>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-dialog v-model="showDialog" width="900px" :title="opaType == 'add' ? '新增' : '编辑' ">
|
||||||
|
<el-form label-position="right" label-width="110px">
|
||||||
|
<el-form-item label="仓库名称:">
|
||||||
|
<el-input v-model="itemInfo.name" clearable></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="仓库类型:">
|
||||||
|
<el-radio-group v-model="itemInfo.type">
|
||||||
|
<el-radio label="cloud">云仓</el-radio>
|
||||||
|
<el-radio label="local">本地仓</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="仓库状态:">
|
||||||
|
<el-radio-group v-model="itemInfo.status">
|
||||||
|
<el-radio :label="1">启用</el-radio>
|
||||||
|
<el-radio :label="0">不启用</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="备注:">
|
||||||
|
<el-input v-model="itemInfo.note" type="textarea" :rows="4"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="排序:">
|
||||||
|
<el-input-number v-model="itemInfo.sort" :min="0" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="showDialog = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="commitOpa()" :loading="opa_loading">确定</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { onMounted, reactive, toRefs, ref, nextTick } from "vue"
|
||||||
|
import { get, post } from "@/api/request"
|
||||||
|
import { Search, Plus, Edit, ZoomIn, Delete } from '@element-plus/icons'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import { parseErrors } from 'components/common'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Search, Plus, Edit, ZoomIn, Delete
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const data = reactive({
|
||||||
|
goodsList: [],
|
||||||
|
page: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0,
|
||||||
|
loading: false,
|
||||||
|
pickerTime: [],
|
||||||
|
opaType: '',
|
||||||
|
showDialog: false,
|
||||||
|
opa_loading: false,
|
||||||
|
Type: {
|
||||||
|
'cloud': '云仓',
|
||||||
|
'local': '本地仓'
|
||||||
|
},
|
||||||
|
itemInfo: {}
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleSearch() {
|
||||||
|
data.page = 1
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchData = () => {
|
||||||
|
data.loading = true
|
||||||
|
let params = {
|
||||||
|
page: data.page,
|
||||||
|
pageSize: data.pageSize,
|
||||||
|
service_id: data.service_id,
|
||||||
|
start_date: data.pickerTime ? data.pickerTime[0] : '',
|
||||||
|
end_date: data.pickerTime ? data.pickerTime[1] : ''
|
||||||
|
}
|
||||||
|
get(`/api/warehouses`, params).then((res) => {
|
||||||
|
data.goodsList = res.data
|
||||||
|
data.total = res.meta.total
|
||||||
|
data.loading = false
|
||||||
|
}).catch(() => {
|
||||||
|
data.loading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCurrentChange(e) {
|
||||||
|
data.page = e
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSizeChange(e) {
|
||||||
|
data.page = 1
|
||||||
|
data.pageSize = e
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleAdd() {
|
||||||
|
data.opaType = 'add'
|
||||||
|
data.itemInfo = {
|
||||||
|
status: 1,
|
||||||
|
sort: 0,
|
||||||
|
type: 'cloud'
|
||||||
|
}
|
||||||
|
data.showDialog = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleEdit(item) {
|
||||||
|
data.opaType = 'edit'
|
||||||
|
data.itemInfo = JSON.parse(JSON.stringify(item))
|
||||||
|
data.showDialog = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function commitOpa() {
|
||||||
|
data.opa_loading = true
|
||||||
|
let params = {
|
||||||
|
...data.itemInfo
|
||||||
|
}
|
||||||
|
if(data.opaType == 'add') {
|
||||||
|
post(`/api/warehouses`, params).then(() => {
|
||||||
|
data.page = 1
|
||||||
|
fetchData()
|
||||||
|
ElMessage({ type: 'success', message: '新增成功' })
|
||||||
|
data.showDialog = false
|
||||||
|
data.opa_loading = false
|
||||||
|
}).catch((err) => {
|
||||||
|
data.opa_loading = false
|
||||||
|
parseErrors(err.response.data.errors)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
post(`/api/warehouses/${data.itemInfo.id}`, params, 'PUT').then(() => {
|
||||||
|
fetchData()
|
||||||
|
ElMessage({ type: 'success', message: '编辑成功' })
|
||||||
|
data.showDialog = false
|
||||||
|
data.opa_loading = false
|
||||||
|
}).catch((err) => {
|
||||||
|
data.opa_loading = false
|
||||||
|
parseErrors(err.response.data.errors)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchData()
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toRefs(data),
|
||||||
|
handleSearch,
|
||||||
|
handleCurrentChange,
|
||||||
|
handleSizeChange,
|
||||||
|
fetchData,
|
||||||
|
handleAdd,
|
||||||
|
handleEdit,
|
||||||
|
commitOpa,
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.searchBox{
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 15px 0 0 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
.row{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 20%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
&.row1{
|
||||||
|
width: 25%;
|
||||||
|
}
|
||||||
|
.span{
|
||||||
|
display: block;
|
||||||
|
width: 80px;
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: right;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.right{
|
||||||
|
width: calc(100% - 100px);
|
||||||
|
}
|
||||||
|
.wid100{
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.opaBox{
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
.imgBox{
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
.el-image{
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.skuBox{
|
||||||
|
border: 1px solid #e5e5e5;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 15px 0;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
background-color: #f3f3f3;
|
||||||
|
.tit{
|
||||||
|
padding-left: 40px;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 15px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
191
src/views/warehouse/io.vue
Normal file
191
src/views/warehouse/io.vue
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
<template>
|
||||||
|
<div class="pageBox">
|
||||||
|
<!-- <div class="searchBox">
|
||||||
|
<div class="row">
|
||||||
|
<span class="span">商品名称:</span>
|
||||||
|
<div class="right"><el-input v-model="goods_name" class="wid100" clearable></el-input></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<el-button type="primary" @click="handleSearch"><el-icon><Search /></el-icon> 筛选</el-button>
|
||||||
|
</div>
|
||||||
|
</div> -->
|
||||||
|
|
||||||
|
<el-card shadow="never">
|
||||||
|
<!-- <div class="opaBox">
|
||||||
|
<el-button type="warning" @click="handleExport"><span class="iconfont icon-daochu"></span> 导出</el-button>
|
||||||
|
</div> -->
|
||||||
|
<el-table :data="goodsList" style="width: 100%" border v-loading="loading">
|
||||||
|
<el-table-column prop="id" label="ID" width="80" align="center" />
|
||||||
|
<el-table-column prop="target_type" label="来源" align="center" />
|
||||||
|
<el-table-column prop="goods.title" label="商品名称" align="center" />
|
||||||
|
<el-table-column prop="goods_id" label="商品编码" align="center" />
|
||||||
|
<el-table-column prop="sku.title" label="规格名称" align="center" />
|
||||||
|
<el-table-column prop="sku.sku_code" label="规格编码" align="center" />
|
||||||
|
<el-table-column prop="num" label="数量" align="center" />
|
||||||
|
<el-table-column prop="cost" label="成本" align="center" />
|
||||||
|
<el-table-column prop="production_date" label="生产日期" align="center" />
|
||||||
|
<el-table-column prop="factory_date" label="出厂日期" align="center" />
|
||||||
|
<el-table-column prop="validity" label="有效时间" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<span>{{scope.row.validity_type == 'none' ? '-' : scope.row.validity}}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="validity_type" label="有效期类型" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<span>{{Validity[scope.row.validity_type]}}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="validity_end_time" label="截止有效期" align="center" />
|
||||||
|
<el-table-column prop="batch_number" label="批次号" align="center" />
|
||||||
|
<el-table-column prop="created_at" label="操作时间" align="center" />
|
||||||
|
</el-table>
|
||||||
|
<div class="page-pagination">
|
||||||
|
<el-pagination
|
||||||
|
:current-page="page"
|
||||||
|
background
|
||||||
|
layout="prev, pager, next, sizes, total"
|
||||||
|
:total="total"
|
||||||
|
:page-sizes="[10, 50, 100]"
|
||||||
|
:page-size="pageSize"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange"></el-pagination>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { onMounted, reactive, toRefs } from "vue"
|
||||||
|
import { get, post } from "@/api/request"
|
||||||
|
import { Search, Plus, Edit, ZoomIn, Delete } from '@element-plus/icons'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Search, Plus, Edit, ZoomIn, Delete
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const data = reactive({
|
||||||
|
goodsList: [],
|
||||||
|
page: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0,
|
||||||
|
loading: false,
|
||||||
|
Validity: {
|
||||||
|
'day': '天',
|
||||||
|
'month': '月',
|
||||||
|
'year': '年',
|
||||||
|
'none': '长期'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleSearch() {
|
||||||
|
data.page = 1
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchData = () => {
|
||||||
|
data.loading = true
|
||||||
|
let params = {
|
||||||
|
page: data.page,
|
||||||
|
pageSize: data.pageSize,
|
||||||
|
service_id: data.service_id,
|
||||||
|
start_date: data.pickerTime ? data.pickerTime[0] : '',
|
||||||
|
end_date: data.pickerTime ? data.pickerTime[1] : ''
|
||||||
|
}
|
||||||
|
get(`/api/goods-put-records`, params).then((res) => {
|
||||||
|
data.goodsList = res.data
|
||||||
|
data.total = res.meta.total
|
||||||
|
data.loading = false
|
||||||
|
}).catch(() => {
|
||||||
|
data.loading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCurrentChange(e) {
|
||||||
|
data.page = e
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSizeChange(e) {
|
||||||
|
data.page = 1
|
||||||
|
data.pageSize = e
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchData()
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toRefs(data),
|
||||||
|
handleSearch,
|
||||||
|
handleCurrentChange,
|
||||||
|
handleSizeChange,
|
||||||
|
fetchData,
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.searchBox{
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 15px 0 0 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
.row{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 20%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
&.row1{
|
||||||
|
width: 25%;
|
||||||
|
}
|
||||||
|
.span{
|
||||||
|
display: block;
|
||||||
|
width: 80px;
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: right;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.right{
|
||||||
|
width: calc(100% - 100px);
|
||||||
|
}
|
||||||
|
.wid100{
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.opaBox{
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
.imgBox{
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
.el-image{
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.skuBox{
|
||||||
|
border: 1px solid #e5e5e5;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 15px 0;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
background-color: #f3f3f3;
|
||||||
|
.tit{
|
||||||
|
padding-left: 40px;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 15px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
63
vue.config.js
Normal file
63
vue.config.js
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
const webpack = require('webpack')
|
||||||
|
const path = require('path')
|
||||||
|
|
||||||
|
function resolve(dir) {
|
||||||
|
return path.join(__dirname, dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
lintOnSave: false,
|
||||||
|
publicPath: "./", //配置打包之后的相对路径
|
||||||
|
devServer: {
|
||||||
|
open: true, // npm run serve后自动打开页面
|
||||||
|
// host: 'localhost',
|
||||||
|
port: 8080, // 开发服务器运行端口号
|
||||||
|
overlay: {
|
||||||
|
warnings: false,
|
||||||
|
errors: true
|
||||||
|
},
|
||||||
|
proxy: { //配置代理
|
||||||
|
"/api": {
|
||||||
|
// target: "http://192.168.0.52:86", // 本地
|
||||||
|
// target: "http://192.168.99.5:8093", // 本地
|
||||||
|
// target: "http://43.138.23.240:82", // 本地//
|
||||||
|
// target: "http://192.168.99.72:81", // 本地//
|
||||||
|
target: 'http://warehouse.chutang66.com/',
|
||||||
|
changeOrigin: true, //开启代理
|
||||||
|
pathRewrite: { // 重命名
|
||||||
|
"^/api": "api"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
chainWebpack: (config) => {
|
||||||
|
config.resolve.alias
|
||||||
|
.set('@', resolve('src'))
|
||||||
|
.set('api', resolve('src/api'))
|
||||||
|
.set('components', resolve('src/components'))
|
||||||
|
.set('views', resolve('src/views'))
|
||||||
|
config.plugin('provide').use(webpack.ProvidePlugin, [{
|
||||||
|
$: 'jquery',
|
||||||
|
jquery: 'jquery',
|
||||||
|
jQuery: 'jquery',
|
||||||
|
'window.jQuery': 'jquery'
|
||||||
|
}])
|
||||||
|
config.plugin('define').tap((definitions) => {
|
||||||
|
Object.assign(definitions[0], {
|
||||||
|
__VUE_OPTIONS_API__: 'true',
|
||||||
|
__VUE_PROD_DEVTOOLS__: 'false',
|
||||||
|
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: 'false'
|
||||||
|
})
|
||||||
|
return definitions
|
||||||
|
})
|
||||||
|
},
|
||||||
|
configureWebpack: () => {
|
||||||
|
var obj = {
|
||||||
|
externals: {
|
||||||
|
'./cptable': 'var cptable',
|
||||||
|
'../xlsx.js': 'var _XLSX'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return obj
|
||||||
|
},
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user