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
|
||||
# gitignore template for Vue.js projects
|
||||
#
|
||||
# Recommended template: Node.gitignore
|
||||
.DS_Store
|
||||
node_modules
|
||||
/dist
|
||||
|
||||
# TODO: where does this rule come from?
|
||||
docs/_book
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# TODO: where does this rule come from?
|
||||
test/
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
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