登录注册界面修复与优化

master
Eterlaze 8 months ago
commit c107824a29

23
Vue/.gitignore vendored

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

11214
Vue/package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -0,0 +1,33 @@
{
"name": "Eterlaze",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build"
},
"dependencies": {
"ant-design-vue": "^1.7.4",
"axios": "^0.21.1",
"echarts": "^5.0.2",
"element-ui": "^2.15.12",
"nprogress": "^0.2.0",
"vue": "^2.6.11",
"vue-json-excel": "^0.3.0",
"vue-router": "^3.2.0",
"vuex": "^3.4.0"
},
"devDependencies": {
"@vue/cli-plugin-router": "~4.5.0",
"@vue/cli-plugin-vuex": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"vue-template-compiler": "^2.6.11"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
],
"main": "index.js",
"license": "MIT"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>圆桌物流管理系统</title>
</head>
<body>
<div id="app"></div>
</body>
</html>

@ -0,0 +1,19 @@
<template>
<div id="app">
<router-view/>
</div>
</template>
<style>
/*顶部进度条样式*/
#nprogress .bar {
background: #1890ff !important;
}
body {
letter-spacing: 1px;
background: #f0f2f5 !important;
}
</style>

@ -0,0 +1,54 @@
import service from "../utils/request";
export function IsInit() {
return service({
url: '/admin/hasInit',
method: 'get',
})
}
export function Init(data) {
return service({
url: '/admin/init',
method: 'post',
data: data
})
}
export function DeleteAdmin(id) {
return service({
url: '/admin?id=' + id,
method: 'delete',
})
}
export function FindAllAdmin() {
return service({
url: '/admin',
method: 'get',
})
}
export function SaveAdmin(data) {
return service({
url: '/admin',
method: 'post',
data: data
})
}
export function AdminSendEmail(email) {
return service({
url: '/admin/sendEmail?email=' + email,
method: 'get'
})
}
//管理员登录
export function AdminLogin(type, data) {
return service({
url: '/admin/login?type=' + type,
method: 'post',
data: data
})
}

@ -0,0 +1,47 @@
import service from "../utils/request";
export function FindAllCommodity() {
console.log("执行FindAllCommodity");
// request 拦截器
// 可以自请求发送前对请求做一些处理
// 比如统一加token对请求参数统一加密
service.interceptors.request.use(config => {
console.log("执行config")
let token = localStorage.getItem("token");
if (token != null){
config.headers['Authorization'] = token; // 设置请求头
}
return config
}, error => {
return Promise.reject(error)
});
return service({
url: '/commodity',
method: 'get'
// headers: {'Authorization': localStorage.getItem("token")}
})
}
export function SearchCommodity(name) {
return service({
url: '/commodity/search/' + name,
method: 'get'
})
}
export function SaveCommodity(data) {
return service({
url: '/commodity',
method: 'post',
data: data
})
}
export function DeleteCommodityById(id) {
return service({
url: '/commodity?id=' + id,
method: 'delete'
})
}

@ -0,0 +1,23 @@
import service from "../utils/request";
export function FindAllDistribution() {
return service({
url: '/distribution',
method: 'get'
})
}
export function FindAllCanUse() {
return service({
url: '/distribution/can',
method: 'get'
})
}
export function SaveDistribution(data) {
return service({
url: '/distribution',
method: 'post',
data: data
})
}

@ -0,0 +1,23 @@
import service from "../utils/request";
export function FindAllDriver(){
return service({
url: '/driver',
method: 'get'
})
}
export function SaveDriver(data) {
return service({
url: '/driver',
method: 'post',
data: data
})
}
export function DeleteDriverById(id){
return service({
url: '/driver?id=' + id,
method: 'delete'
})
}

@ -0,0 +1,23 @@
import service from "../utils/request";
export function FindAllEmployee(){
return service({
url: '/employee',
method: 'get'
})
}
export function SaveEmployee(data) {
return service({
url: '/employee',
method: 'post',
data: data
})
}
export function DeleteEmployeeById(id){
return service({
url: '/employee?id=' + id,
method: 'delete'
})
}

@ -0,0 +1,37 @@
import service from "../utils/request";
export function AnalyzeCommodity(type) {
return service({
url: '/inventory/analyze?type=' + type,
method: 'get'
})
}
export function FindAllInventory(id) {
return service({
url: '/inventory/warehouse/' + id,
method: 'get'
})
}
export function InAndOut(type, data) {
return service({
url: '/inventory/' + type,
method: 'post',
data: data
})
}
export function FindRecordByWarehouse(id) {
return service({
url: '/inventory/record/warehouse/' + id,
method: 'get'
})
}
export function DeleteInventoryById(id) {
return service({
url: '/inventory?id=' + id,
method: 'delete'
})
}

@ -0,0 +1,46 @@
/*
日志管理请求模块
*/
import service from "../utils/request";
//查询全部登录日志
export function FindAllLoginLog() {
return service({
url: '/loginlog',
method: 'get'
})
}
//查询全部操作日志
export function FindAllSystemLog() {
return service({
url: '/systemlog',
method: 'get'
})
}
//根据条件查询操作日志
export function querySystemLog(account,moudle) {
return service({
url: 'querySystemlog?account='+account+'&moudle='+moudle,
method: 'get'
})
}
//删除操作日志
export function DeleteSystemLogById(id) {
return service({
url: '/systemlog?id='+id,
method: 'delete'
})
}
//删除登录日志
export function DeleteLoginLogById(id) {
return service({
url: '/loginlog?id='+id,
method: 'delete'
})
}

@ -0,0 +1,8 @@
import service from "../utils/request";
export function FindAllRole(){
return service({
url: '/role',
method: 'get'
})
}

@ -0,0 +1,24 @@
import service from "../utils/request";
export function FindAllSale() {
console.log("FindAllSale")
return service({
url: '/sale',
method: 'get'
})
}
export function SearchCompany(name) {
return service({
url: '/sale/search/' + name,
method: 'get'
})
}
export function SaveSale(data) {
return service({
url: '/sale',
method: 'post',
data: data
})
}

@ -0,0 +1,23 @@
import service from "../utils/request";
export function FindAllVehicle(){
return service({
url: '/vehicle',
method: 'get'
})
}
export function SaveVehicle(data) {
return service({
url: '/vehicle',
method: 'post',
data: data
})
}
export function DeleteVehicleById(id){
return service({
url: '/vehicle?id=' + id,
method: 'delete'
})
}

@ -0,0 +1,23 @@
import service from "../utils/request";
export function FindAllWarehouse() {
return service({
url: '/warehouse',
method: 'get'
})
}
export function SaveWarehouse(data) {
return service({
url: '/warehouse',
method: 'post',
data: data
})
}
export function DeleteWarehouseById(id) {
return service({
url: '/warehouse?id=' + id,
method: 'delete'
})
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 458 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.3 KiB

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1616761194351" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1748" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M364 584h308v16H364z" fill="#0942F0" p-id="1749"></path><path d="M364 616h308v16H364zM364 648h308v16H364zM364 680h308v16H364zM364 712h308v16H364zM364 744h308v16H364zM364 776h308v16H364zM364 808h308v16H364zM364 840h308v16H364zM364 872h308v16H364z" fill="#0942F0" p-id="1750"></path><path d="M688 887.2V568H348v319.2H144v-468l370-161.2 374 161.32v468z" fill="#0942F0" p-id="1751"></path><path d="M516 193.44l372 160.2v51.24L516 244l-372 161.68V354.44l372-161.08z" fill="#47BAFF" p-id="1752"></path></svg>

After

Width:  |  Height:  |  Size: 879 B

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1616760982063" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2269" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M526.719 130.624c-5.293-5.293-20.304-4.862-26.462 0L98.043 448.161c-10.586 10.584-10.586 21.171-5.293 26.464h74.092v370.459c0 31.753 21.169 52.922 52.922 52.922h79.385V580.47h423.387v317.537h79.381c31.757 0 52.927-21.169 52.927-52.922v-370.46h74.092c5.289-5.293 5.289-15.88 0-26.464L526.719 130.624z m402.217 317.537" fill="#009688" p-id="2270" data-spm-anchor-id="a313x.7781069.0.i1" class="selected"></path><path d="M537.302 739.24H484.38v-52.926H378.535v211.692H643.15V686.314H537.302v52.926z m0 0" fill="#009688" p-id="2271"></path></svg>

After

Width:  |  Height:  |  Size: 919 B

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1616761200155" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1890" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M281.6 373.76l251.4176-204.8 235.7504 204.8a76.8 76.8 0 0 1 76.8 76.8v384a76.8 76.8 0 0 1-76.8 76.8H281.6a76.8 76.8 0 0 1-76.8-76.8v-384a76.8 76.8 0 0 1 76.8-76.8z" fill="#47B89C" p-id="1891"></path><path d="M947.2 588.8l-422.4-370.6368L102.4 588.8v-115.8144l422.4-370.5856 422.4 370.5856V588.8z m-316.8256 0H102.4h527.9744z" fill="#F4CE73" p-id="1892"></path><path d="M486.4 588.8m25.6 0l256 0q25.6 0 25.6 25.6l0 230.4q0 25.6-25.6 25.6l-256 0q-25.6 0-25.6-25.6l0-230.4q0-25.6 25.6-25.6Z" fill="#FFFFFF" p-id="1893"></path><path d="M788.48 873.8816h-296.704a34.176 34.176 0 0 1-5.376-0.4352v-59.3664l35.84-44.5952a30.3872 30.3872 0 0 1 20.0448-12.3392 28.9536 28.9536 0 0 1 19.4304 11.1616l33.024 37.6832a15.8976 15.8976 0 0 0 12.8 6.4h0.5376a13.44 13.44 0 0 0 11.4688-6.4l62.72-96.7168a25.6 25.6 0 0 1 18.1504-7.68 31.0272 31.0272 0 0 1 3.4048 0.2304 22.016 22.016 0 0 1 2.7392-0.1536 20.48 20.48 0 0 1 16.0768 7.9104l70.9632 94.6176v69.2992a35.4304 35.4304 0 0 1-5.12 0.384z m-205.9008-153.0624a40.4224 40.4224 0 1 1 40.3712-40.448 40.448 40.448 0 0 1-40.3712 40.448z" fill="#1694F6" p-id="1894"></path><path d="M281.6 537.6h76.8v76.8h-76.8z" fill="#FFFFFF" p-id="1895"></path><path d="M281.6 665.6h76.8v76.8h-76.8z" fill="#FFFFFF" p-id="1896"></path><path d="M281.6 793.6h76.8v76.8h-76.8z" fill="#FFFFFF" p-id="1897"></path></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1616764662795" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2987" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M672 400h80v352h-80z" fill="#5C546A" p-id="2988"></path><path d="M960 784H64c-17.674 0-32-14.326-32-32v-32h960v32c0 17.674-14.326 32-32 32z" fill="#5C546A" p-id="2989"></path><path d="M984.062 494.918l-155.766-217.16A16.016 16.016 0 0 0 816 272h-64c-8.844 0-16 7.164-16 16v432c0 8.836 7.156 16 16 16h224c26.468 0 48-21.532 48-48v-145.762c0-23.726-17.296-43.484-39.938-47.32z" fill="#FF4F19" p-id="2990"></path><path d="M893.404 288c20.66 0 39.004 13.22 45.536 32.822l45.122 174.096s-115.088-23.386-148.982-32.68C814.286 456.538 800 437.56 800 416v-128h93.404z" fill="#5C546A" p-id="2991"></path><path d="M800 304c-8.844 0-16-7.164-16-16v-76.118c0-8.54-3.328-16.578-9.376-22.632l-17.938-17.938c-6.25-6.25-6.25-16.376 0-22.626s16.376-6.25 22.626 0l17.938 17.938c12.094 12.102 18.75 28.172 18.75 45.258V288c0 8.836-7.156 16-16 16z" fill="#8A8895" p-id="2992"></path><path d="M656 208H48c-26.468 0-48 21.532-48 48v432c0 26.468 21.532 48 48 48h640c8.844 0 16-7.164 16-16V256c0-26.468-21.532-48-48-48z" fill="#527991" p-id="2993"></path><path d="M0 688c0 26.468 21.532 48 48 48h640c8.844 0 16-7.164 16-16v-112H0v80z" fill="#5D647F" p-id="2994"></path><path d="M496 816h-128c-17.674 0-32-14.326-32-32v-32c0-17.674 14.326-32 32-32h128c17.674 0 32 14.326 32 32v32c0 17.674-14.326 32-32 32z" fill="#8A8895" p-id="2995"></path><path d="M384 784c-4.156 0-8.328-1.766-11.36-4.64-2.89-3.04-4.64-7.204-4.64-11.36 0-4.164 1.75-8.32 4.64-11.36 6.078-5.922 16.796-5.922 22.718 0 2.876 3.04 4.64 7.196 4.64 11.36 0 4.156-1.766 8.32-4.796 11.36-2.89 2.874-7.046 4.64-11.202 4.64z" fill="#5C546A" p-id="2996"></path><path d="M968 608c-13.254 0-24 10.746-24 24s10.746 24 24 24h24v-48h-24z" fill="#FFD100" p-id="2997"></path><path d="M192 768m-112 0a112 112 0 1 0 224 0 112 112 0 1 0-224 0Z" fill="#5C546A" p-id="2998"></path><path d="M192 768m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0Z" fill="#8A8895" p-id="2999"></path><path d="M832 768m-112 0a112 112 0 1 0 224 0 112 112 0 1 0-224 0Z" fill="#5C546A" p-id="3000"></path><path d="M832 768m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0Z" fill="#8A8895" p-id="3001"></path><path d="M992 608h32v48h-32z" fill="#FFFFFF" p-id="3002"></path><path d="M944 272h-192a16 16 0 0 0-16 16v16h208c8.844 0 16-7.164 16-16s-7.156-16-16-16z" fill="#E7001E" p-id="3003"></path></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.2 KiB

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1616764699644" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4002" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M602.69 709.22l32.45-289.83 155.2-0.17 71.61 105.46 64.89 32.58-0.32 151.96z" fill="#E6E9ED" p-id="4003"></path><path d="M852.39 510.6l-62.05-91.38-155.2 0.17-10.19 91.21z" fill="#4FC2E9" p-id="4004"></path><path d="M874.29 510.6l-74.35-109.51-181.03 0.2-36.48 326.05H944.6l0.4-181.25-70.71-35.49z m34.15 180.49l-285.46-0.01 28.36-253.58 129.41-0.15 63.56 93.61 5.29 7.79 8.42 4.23 50.66 25.44-0.24 122.67z" fill="#E6E9ED" p-id="4005"></path><path d="M618.71 219.84H111.2c-20.02 0-36.24 16.22-36.24 36.24v435c0 20.03 16.22 36.26 36.24 36.26h507.48c20.02 0 36.27-16.22 36.27-36.26v-435c0-20.01-16.22-36.24-36.24-36.24z" fill="#FC6E51" p-id="4006"></path><path d="M691.22 437.43h36.24v73.16h-36.24z" fill="#3BAFDA" p-id="4007"></path><path d="M183.28 219.44h36.24v504.85h-36.24zM292.47 219.44h36.24v504.85h-36.24zM401.65 219.44h36.24v504.85h-36.24zM510.83 219.44h36.24v504.85h-36.24z" fill="#E9573F" p-id="4008"></path><path d="M781.81 710.1m-90.62 0a90.62 90.62 0 1 0 181.24 0 90.62 90.62 0 1 0-181.24 0Z" fill="#434A54" p-id="4009"></path><path d="M256.2 710.1m-90.62 0a90.62 90.62 0 1 0 181.24 0 90.62 90.62 0 1 0-181.24 0Z" fill="#434A54" p-id="4010"></path><path d="M763.68 710.1a18.13 18.12 0 1 0 36.26 0 18.13 18.12 0 1 0-36.26 0Z" fill="#F5F7FA" p-id="4011"></path><path d="M238.06 710.1c0 10.01 8.12 18.12 18.13 18.12s18.11-8.11 18.11-18.12c0-10.01-8.1-18.12-18.11-18.12-10 0-18.13 8.12-18.13 18.12z" fill="#F5F7FA" p-id="4012"></path><path d="M944.92 582.34H908.7c-10.01 0-18.11 8.11-18.11 18.12 0 10.01 8.1 18.12 18.11 18.12h36.14l0.08-36.24z" fill="#ED5564" p-id="4013"></path></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

@ -0,0 +1,94 @@
<template>
<div>
<download-excel
class="export-excel-wrapper"
:data="data"
:fields="json_fields"
name="出入库记录.xls">
<a-button class="btn" icon="download" type="primary">下载Excel表格</a-button>
</download-excel>
<a-table :columns="columns" :data-source="data" rowKey="id">
<span slot="type" slot-scope="type">
<a-tag :color="type === -1 ? 'green' : 'cyan'">{{ type === -1 ? '出库' : '入库' }}</a-tag>
</span>
</a-table>
</div>
</template>
<script>
import {FindRecordByWarehouse} from "../api/inventory";
const columns = [
{
dataIndex: 'name',
key: 'name',
title: '商品',
},
{
title: '数量',
dataIndex: 'count',
key: 'count',
},
{
title: '时间',
dataIndex: 'createAt',
key: 'createAt',
},
{
title: '类型',
key: 'type',
dataIndex: 'type',
scopedSlots: {customRender: 'type'},
},
{
title: '备注',
dataIndex: 'description',
key: 'description',
},
];
export default {
components: {},
props: {
warehouseId: {type: String, default: ''},
},
data() {
return {
data: [],
columns,
json_fields: {
"商品ID": "cid",
"商品名称": "name",
"类型": {
field: 'type',
callback: (value) => {
console.log(value)
return value === -1 ? '出库' : '入库'
}
},
"数量": "count",
"备注": "description",
"时间": "createAt",
},
}
},
mounted() {
FindRecordByWarehouse(this.warehouseId).then((res) => {
this.data = res.data
console.log(res.data[0])
})
},
}
</script>
<style scoped>
.btn {
margin-bottom: 15px;
letter-spacing: 1px;
}
</style>

@ -0,0 +1,119 @@
<template>
<a-layout-sider
:style="{ overflow: 'auto', height: '100vh', position: 'fixed', left: 0 }"
width="220">
<div class="logo">
圆桌物流管理系统
</div>
<a-menu theme="dark" mode="inline">
<a-sub-menu v-for="(item, index) in menus" :key="index">
<span slot="title">
<a-icon :type="item.icon"/>
<span>{{ item.title }}</span>
</span>
<a-menu-item v-for="menu in item.children" :key="menu.title">
<router-link :to="menu.path">
{{ menu.title }}
</router-link>
</a-menu-item>
</a-sub-menu>
</a-menu>
</a-layout-sider>
</template>
<script>
export default {
data() {
return {
menus: [
{
title: '基础管理',
icon: 'home',
children: [
{title: '商品管理', path: '/commodity'},
{title: '来往单位', path: '/company'},
{title: '员工管理', path: '/employee'},
{title: '仓库管理', path: '/warehouse'},
]
},
{
title: '销售管理',
icon: 'pay-circle',
children: [
{title: '销售开票', path: '/sale/create'},
{title: '销售记录', path: '/sale/record'},
]
},
{
title: '配送管理',
icon: 'car',
children: [
{title: '申请配送', path: '/delivery/create'},
{title: '配送列表', path: '/delivery/list'},
]
},
{
title: '运输管理',
icon: 'rocket',
children: [
{title: '车辆资料', path: '/vehicle'},
{title: '驾驶员资料', path: '/driver'},
]
},
{
title: '图表分析',
icon: 'line-chart',
children: [
{title: '入库分析', path: '/analyze/in'},
{title: '出库分析', path: '/analyze/out'},
]
},
{
title: '系统管理',
icon: 'tool',
children: [
{title: '安全设置', path: '/security'},
{title: '操作员管理', path: '/admin'},
{title: '权限列表', path: '/role'}
]
},
{
title: '日志管理',
icon: 'file',
children: [
{title: '登录日志', path: '/loginlog'},
{title: '操作日志', path: '/systemlog'}
]
}
]
}
},
}
</script>
<style scoped>
#components-layout-demo-fixed-sider .logo {
padding: 10px 15px;
height: 50px;
font-size: 15px;
margin: 16px;
color: #ffffff;
letter-spacing: 2px;
}
.ant-menu {
letter-spacing: 1px;
}
.logo img {
width: 32px;
height: 32px;
margin-right: 5px;
}
</style>

@ -0,0 +1,15 @@
<template>
<a-layout-footer :style="{ textAlign: 'center', letterSpacing: '1px' }">
圆桌物流管理系统
</a-layout-footer>
</template>
<script>
export default {
name: "Footer"
}
</script>
<style scoped>
</style>

@ -0,0 +1,111 @@
<template>
<a-layout-header class="header" :style="{ background: '#fff', padding: 0}">
<a-tooltip placement="bottom">
<template slot="title">
<span>刷新页面</span>
</template>
<!-- <a-button @click="clickReload" class="reload-btn" icon="reload" type="link">Reload</a-button> -->
</a-tooltip>
<a-tooltip placement="bottom" class="question-btn">
<template slot="title">
<span>About</span>
</template>
<a-button class="reload-btn" icon="question-circle" type="link"/>
</a-tooltip>
<a-dropdown>
<a-button class="avatar-btn" type="link">
<div class="avatar-btn-main">
<a-avatar class="avatar"
:size="26"
src="https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png"/>
<div class="avatar-btn-username">{{ details.email }}</div>
</div>
</a-button>
<a-menu slot="overlay">
<a-menu-item>
<router-link to="/security">
<a-icon class="icon" type="smile"/>
个人中心
</router-link>
</a-menu-item>
<a-menu-item>
<router-link to="/role">
<a-icon class="icon" type="setting"/>
权限设置
</router-link>
</a-menu-item>
<a-menu-divider/>
<a-menu-item @click="handleLogout">
<a-icon class="icon" type="logout"/>
退出登录
</a-menu-item>
</a-menu>
</a-dropdown>
</a-layout-header>
</template>
<script>
export default {
data() {
return {
details: this.$store.state.user.details ? this.$store.state.user.details : {}
}
},
methods: {
clickReload() {
this.$router.go(0)
},
handleLogout() {
let that = this
this.$confirm({
title: '你确定要注销你的登录信息吗?',
content: '点击确定将删除你在网站保留的登录信息!',
onOk() {
that.$router.push('/login')
that.$store.commit('user/userLogout')
},
});
}
}
}
</script>
<style scoped>
.reload-btn {
font-size: 15px;
margin-left: 10px;
color: rgba(0, 0, 0, 0.65);
}
.question-btn {
float: right;
margin: 15px 12px 0 0;
}
.avatar-btn {
float: right;
font-size: 15px;
color: rgba(0, 0, 0, 0.55);
height: 64px;
}
.avatar-btn-main {
display: flex;
}
.avatar-btn-username {
line-height: 62px;
font-size: 14px;
}
.avatar {
margin-right: 12px;
margin-top: 19px;
}
</style>

@ -0,0 +1,49 @@
<template>
<a-layout id="components-layout-demo-fixed-sider">
<Aside/>
<a-layout :style="{ marginLeft: '220px' }">
<Header/>
<Main/>
<Footer/>
</a-layout>
</a-layout>
</template>
<script>
import Aside from "@/layout/Aside";
import Header from "@/layout/Header";
import Main from "@/layout/Main";
import Footer from "@/layout/Footer";
export default {
components: {Aside, Header, Main, Footer},
mounted() {
this.$message.success(
'欢迎管理员 ' + this.$store.state.user.details.email,6,
);
}
}
</script>
<style scoped>
#components-layout-demo-fixed-sider .logo {
padding: 10px 15px;
height: 50px;
font-size: 15px;
margin: 16px;
color: #ffffff;
letter-spacing: 2px;
}
.logo img {
width: 32px;
height: 32px;
margin-right: 5px;
}
.header {
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1)
}
</style>

@ -0,0 +1,9 @@
<template>
<a-layout-content :style="{ margin: '24px', overflow: 'initial' }">
<router-view/>
</a-layout-content>
</template>
<style scoped>
</style>

@ -0,0 +1,19 @@
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import Antd from 'ant-design-vue'
import 'ant-design-vue/dist/antd.css'
import JsonExcel from 'vue-json-excel'
import 'element-ui/lib/theme-chalk/index.css'; // 导入相关样式
Vue.component('downloadExcel', JsonExcel)
Vue.config.productionTip = false
Vue.use(Antd)
new Vue({
router,
store,
render: function (h) { return h(App) }
}).$mount('#app')

@ -0,0 +1,32 @@
import Vue from 'vue'
import VueRouter from 'vue-router'
import store from '../store'
import routes from "@/router/routes"
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
//顶部进度条样式
NProgress.configure({
showSpinner: false,
speed: 800,
});
Vue.use(VueRouter)
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
//路由卫士
router.beforeEach((to, from, next) => {
NProgress.start();
to.meta.auth && !store.state.user.token ? next("/login") : next();
})
router.afterEach(() => {
NProgress.done()
})
export default router

@ -0,0 +1,155 @@
import Layout from '@/layout/Index'
const routes = [
//公共布局下的路由
{
path: '/',
component: Layout,
children: [
{
path: "",
redirect: "/commodity",
meta: {auth: true}
},
{
path: 'commodity',
component: () => import('@/views/basics/Commodity'),
meta: {auth: true}
},
{
path: 'company',
component: () => import('@/views/basics/Company'),
meta: {auth: true}
},
{
path: 'employee',
component: () => import('@/views/basics/Employee'),
meta: {auth: true}
},
{
path: 'warehouse',
component: () => import('@/views/basics/Warehouse'),
meta: {auth: true}
},
{
path: 'warehouse/:id',
component: () => import('@/views/basics/Details'),
meta: {auth: true}
},
{
path: 'driver',
component: () => import('@/views/transport/Driver'),
meta: {auth: true}
},
{
path: 'loginlog',
component: () => import('@/views/logging/LoginLog'),
meta: {auth: true}
},
{
path: 'systemlog',
component: () => import('@/views/logging/SystemLog'),
meta: {auth: true}
},
{
path: 'vehicle',
component: () => import('@/views/transport/Vehicle'),
meta: {auth: true}
},
{
path: 'sale/record',
component: () => import('@/views/sale/Record'),
meta: {auth: true}
},
{
path: 'sale/create',
component: () => import('@/views/sale/Create'),
meta: {auth: true}
},
{
path: 'delivery/create',
component: () => import('@/views/delivery/Create'),
meta: {auth: true}
},
{
path: 'delivery/list',
component: () => import('@/views/delivery/List'),
meta: {auth: true}
},
{
path: 'analyze/in',
component: () => import('@/views/analyze/In'),
meta: {auth: true}
},
{
path: 'analyze/out',
component: () => import('@/views/analyze/Out'),
meta: {auth: true}
},
{
path: 'security',
component: () => import('@/views/system/Security'),
meta: {auth: true},
},
{
path: 'admin',
component: () => import('@/views/system/Admin'),
meta: {auth: true},
},
{
path: 'role',
component: () => import('@/views/system/Role'),
meta: {auth: true},
},
//403
{
path: '403',
component: () => import('@/views/error/403'),
meta: {auth: true}
},
//404
{
path: '404',
component: () => import('@/views/error/404'),
meta: {auth: true}
},
//500
{
path: '500',
component: () => import('@/views/error/500'),
meta: {auth: true}
},
]
},
//登录页
{
path: '/login',
name: 'Login',
component: () => import('@/views/Login'),
meta: {auth: false}
},
//初始化
{
path: '/init',
name: 'Init',
component: () => import('@/views/Init'),
meta: {auth: false}
},
//404
{
path: "*",
redirect: '/404'
}
]
export default routes

@ -0,0 +1,11 @@
import Vue from 'vue'
import Vuex from 'vuex'
import user from './modules/user'
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
user
}
})

@ -0,0 +1,40 @@
const state = {
token: localStorage.getItem("token"),
details: JSON.parse(localStorage.getItem("user"))
}
const getters = {}
const mutations = {
saveToken(state, token) {
state.token = token
localStorage.setItem("token", token)
console.log(localStorage.getItem("token"))
console.log("执行saveToken")
},
saveLoginUser(state, user) {
state.details = user
localStorage.setItem("user", JSON.stringify(user))
console.log("saveLoginUser")
},
userLogout(state) {
state.details = null
state.token = null
localStorage.removeItem("token")
localStorage.removeItem("user")
}
}
const actions = {}
export default {
namespaced: true,
state,
getters,
mutations,
actions
}

@ -0,0 +1,28 @@
import request from "axios"
import {message} from 'ant-design-vue'
import router from '../router/index'
var token = localStorage.getItem("token")
var service = request.create({
baseURL: 'http://localhost:8088/api',
timeout: 50000
});
service.interceptors.response.use(
response => {
const res = response.data;
//判断response状态
if (!res.status) message.error('请求错误: ' + res.msg)
if (res.code === 403) router.push("/403")
return res
},
error => {
message.error(error)
router.push('/500')
return Promise.reject(error)
}
);
console.log("执行request.js2");
export default service

@ -0,0 +1,205 @@
<template>
<div class="login_container">
<div class="login-box">
<div class="box-header">
<div class="box-header-t">账号注册</div>
</div>
<a-input
v-model="form.email"
size="large"
style="margin-top: 10px"
class="input"
placeholder="邮箱">
<a-icon slot="prefix" type="mail"/>
</a-input>
<a-input-password
v-model="form.password"
size="large"
class="input"
placeholder="密码">
<a-icon slot="prefix" type="lock"/>
</a-input-password>
<a-button :loading="submitLoading" class="submit-btn" type="primary" @click="submitLogin">
注册提交
</a-button>
<div class="des"> </div>
<!-- 折叠面板 -->
<a-collapse v-model:activeKey="activeKey" :bordered="false" :show-arrow="true">
<template #expandIcon="{ isActive }">
<caret-right-outlined :rotate="isActive ? 90 : 0" />
</template>
<a-collapse-panel key="1" header="注册须知(点击查看)" :style="customStyle" >
<p>邮箱长度不小于8位密码长度不小于6位</p>
</a-collapse-panel>
</a-collapse>
<router-link to="/login">返回登录页</router-link>
</div>
</div>
</template>
<script>
import {Init, IsInit} from "../api/admin";
// import { CaretRightOutlined } from '@ant-design/icons-vue';
export default {
components:{
// CaretRightOutlined
},
data() {
return {
spinning: true,
sendLoading: false,
submitType: '2', //1 2
submitLoading: false,
form: {
password: '',
email: '',
},
isActive: true,
customStyle: 'background: #f7f7f7;border-radius: 4px;margin-bottom: 24px;border: 0;overflow: hidden'
}
},
mounted() {
//SUPER
// IsInit().then((res) => {
// if (res.data) this.$router.push('/login')
// })
},
methods: {
submitLogin() {
if (this.checkEmail()) {
Init(this.form).then((res) => {
if (res.status){
this.$message.success('注册成功')
this.$router.push('/login')
}
})
}
},
checkEmail() {
const emailRegex = new RegExp('^\\w{3,}(\\.\\w+)*@[A-z0-9]+(\\.[A-z]{2,5}){1,2}$')
if (!emailRegex.test(this.form.email)) {
this.$message.error('请输入正确格式的邮箱');
return false
} else {
return true
}
},
}
}
</script>
<style scoped>
.login_container {
width: 100%;
height: 100vh;
background-image: url(../assets/home.jpg);
background-repeat: no-repeat;
background-size: cover;
display: flex;
justify-content: center; /* 水平居中 */
align-items: center; /* 垂直居中 */
}
body {
background: #000000 !important;
}
.ant-tabs-bar {
border-bottom: none !important;
}
.ant-btn-primary {
border-color: #5a84fd;
}
.login-box {
width: 360px;
height: auto; /* 高度自动,根据内容调整 */
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
background-color: rgba(255, 255, 255, 0.6);
padding: 20px; /* 确保内边距不会过大 */
padding-top: 0px;
margin-top: -10px;
border-radius: 8px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
}
.box-header {
display: flex;
justify-content: center; /* 主轴居中对齐 */
align-items: center; /* 交叉轴居中对齐 */
}
.box-header-t {
font-size: 22px; /* 使用简写形式 */
letter-spacing: 1px;
color: #000000; /* 确保颜色代码正确,这里看起来像是 #ffffff 白色 */
position: relative; /* 相对于其正常位置进行定位 */
margin-top: 20px;
}
.logo {
width: 44px;
height: 44px;
margin-right: 20px;
margin-left: 43px;
}
.ant-tabs-nav {
width: 350px;
}
.ant-tabs-ink-bar {
left: 52px;
}
.ant-input-affix-wrapper .ant-input {
font-size: 12px !important;
}
.input {
margin-bottom: 25px;
font-size: 10px;
}
.code-btn {
height: 40px; /* 如果需要,可以调整高度 */
margin-left: 30px; /* 保持左边距不变 */
margin-bottom: 0px; /* 移除下方按钮的下边距,减少与下方按钮之间的距离 */
margin-top: 0px; /* 减少上方边距,根据需要调整这个值 */
}
.submit-btn {
letter-spacing: 2px;
background: #5a84fd;
width: 100%;
height: 45px;
margin-top: 0px; /* 减少上方边距,根据需要调整这个值 */
}
.des {
padding-top: 0px;
font-size: 13px;
text-align: center;
color: #91949c;
letter-spacing: 2px;
margin-bottom: 20px;
}
</style>

@ -0,0 +1,270 @@
<template>
<div class="login_container">
<div class="login-box">
<div class="title title-with-logo">
<img src="../assets/logo.png" alt="Logo" class="logo">
<span>圆桌物流管理系统</span>
</div>
<a-tabs @change="tabClick" default-active-key="1" :tabBarStyle="{ textAlign: 'center' }">
<a-tab-pane key="1" tab="密码登录">
<a-input
v-model="form.email"
size="large"
style="margin-top: 10px"
class="input"
placeholder="邮箱">
<a-icon slot="prefix" type="mail"/>
</a-input>
<a-input-password
v-model="form.password"
size="large"
class="input"
placeholder="密码">
<a-icon slot="prefix" type="lock"/>
</a-input-password>
</a-tab-pane>
<a-tab-pane key="2" tab="验证码登录" force-render>
<a-input
v-model="form.email"
size="large"
style="margin-top: 10px"
class="input"
placeholder="邮箱">
<a-icon slot="prefix" type="mail"/>
</a-input>
<div style="display: flex">
<a-input
v-model="form.code"
size="large"
class="input"
placeholder="验证码">
<a-icon slot="prefix" type="safety-certificate"/>
</a-input>
<a-button class="code-btn" :loading="sendLoading" @click="sendEmail">
获取验证码
</a-button>
</div>
</a-tab-pane>
</a-tabs>
<div style="margin-bottom: 20px;display: flex;justify-content: space-around;">
<a-checkbox v-model="form.remember" style="display: inline;"></a-checkbox>
<!-- <a-button type="link" to="/init">没有账号点我注册 </a-button> -->
<router-link to="/init" class="custom-router-link">没有账号点我注册</router-link>
</div>
<a-button :loading="submitLoading" class="submit-btn" type="primary" @click="submitLogin">
确认登录
</a-button>
<div class="des">管理员登录</div>
</div>
</div>
</div>
</template>
<script>
import {AdminLogin, AdminSendEmail} from "@/api/admin";
import {IsInit} from "../api/admin";
import service from "../utils/request"
export default {
data() {
return {
sendLoading: false,
submitType: '1', //1 2
submitLoading: false,
form: {
password: '',
email: '',
code: '',
remember: false,
},
}
},
mounted() {
IsInit().then((res) => {
if (!res.data) this.$router.push('/init')
})
},
methods: {
sendEmail() {
if (this.checkEmail()) {
this.sendLoading = true
var that = this;
AdminSendEmail(this.form.email).then((res) => {
if (res.data.status) this.$message.success(res.data.msg)
else this.$message.error(res.data.msg)
that.sendLoading = false
})
}
},
submitLogin() {
if (this.checkEmail()) {
let type = this.submitType === '1' ? "passwrod" : "email"
AdminLogin(type, this.form).then((res) => {
if (res.status) {
//LocalStoreToken
this.$store.commit('user/saveToken', res.data.token)
this.$store.commit('user/saveLoginUser', res.data.admin)
setTimeout(() => {
this.$router.push("/commodity")
this.submitLoading = false
}, 1000)
this.$message.success("登录成功")
}
})
}
},
tabClick(key) {
this.submitType = key
},
checkEmail() {
const emailRegex = new RegExp('^\\w{3,}(\\.\\w+)*@[A-z0-9]+(\\.[A-z]{2,5}){1,2}$')
if (!emailRegex.test(this.form.email)) {
this.$message.error('请输入正确格式的邮箱');
return false
} else {
return true
}
},
}
}
</script>
<style scoped>
.login_container {
width: 100%;
height: 100vh;
background-image: url(../assets/home.jpg);
background-repeat: no-repeat;
background-size: cover;
display: flex;
justify-content: center; /* 水平居中 */
align-items: center; /* 垂直居中 */
}
.ant-tabs-bar {
border-bottom: none !important;
}
::v-deep(.ant-tabs-tab) {
color: #000000; /* 您希望的颜色 */
}
::v-deep(.ant-tabs-ink-bar) {
background-color: #000000; /* 您希望的颜色 */
}
/* 改变标签页文本在鼠标悬停时的颜色 */
::v-deep(.ant-tabs-tab:hover) {
color: #4F4F4F;
}
.ant-btn-primary {
border-color: #5a84fd;
}
.login-box {
width: 360px; /* 例如设置宽度为300像素 */
height: auto; /* 高度自动,根据内容调整 */
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
background-color: rgba(255, 255, 255, 0.6);
padding: 10px;margin-top: 50px;
margin-top: 50px;
border-radius: 8px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
}
.box-header {
display: flex;
}
.box-header-t {
font-weight: bolder;
font-size: 30px;
}
.logo {
width: 44px;
height: 44px;
margin-right: 20px;
margin-left: 43px;
}
.ant-tabs-nav {
width: 350px;
}
.ant-input-affix-wrapper .ant-input {
font-size: 12px !important;
}
.title {
color: rgb(227, 230, 237);
padding-top: 15px;
padding-bottom: 35px;
font-size: 30px;
text-align: center;
}
.input {
margin-bottom: 25px;
font-size: 10px;
}
.code-btn {
height: 40px;
margin-left: 30px;
}
.submit-btn {
letter-spacing: 2px;
background: #5a84fd;
width: 100%;
height: 45px;
}
.des {
padding-top: 25px;
font-size: 14px;
text-align: center;
color: #45474e;
letter-spacing: 2px;
}
.custom-router-link {
color: #CE0000;
text-decoration: none; /* 去除下划线 */
}
.custom-router-link:hover {
color: #FF0000;
text-decoration: underline; /* 悬停时添加下划线 */
}
.title-with-logo {
margin-right: -10px; /* 向左移动50px根据需要调整这个值 */
display: flex;
align-items: center;
color: rgb(227, 230, 237);
font-size: 30px;
position: absolute;
top: -130px; /* 根据需要调整这个值 */
padding: 15px 0; /* 只设置上下内边距,避免影响宽度 */
}
.title-with-logo .logo {
width: 105px; /* 设置logo的宽度 */
height: 105px; /* 设置logo的高度 */
margin-left: -30px; /* logo和文本之间的间距 */
margin-right: 5px;
}
.title-with-logo span {
white-space: nowrap; /* 防止文本换行 */
}
</style>

@ -0,0 +1,87 @@
<template>
<a-form-model :model="form" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-form-model-item label="Activity name">
<a-input v-model="form.name" />
</a-form-model-item>
<a-form-model-item label="Activity zone">
<a-select v-model="form.region" placeholder="please select your zone">
<a-select-option value="shanghai">
Zone one
</a-select-option>
<a-select-option value="beijing">
Zone two
</a-select-option>
</a-select>
</a-form-model-item>
<a-form-model-item label="Activity time">
<a-date-picker
v-model="form.date1"
show-time
type="date"
placeholder="Pick a date"
style="width: 100%;"
/>
</a-form-model-item>
<a-form-model-item label="Instant delivery">
<a-switch v-model="form.delivery" />
</a-form-model-item>
<a-form-model-item label="Activity type">
<a-checkbox-group v-model="form.type">
<a-checkbox value="1" name="type">
Online
</a-checkbox>
<a-checkbox value="2" name="type">
Promotion
</a-checkbox>
<a-checkbox value="3" name="type">
Offline
</a-checkbox>
</a-checkbox-group>
</a-form-model-item>
<a-form-model-item label="Resources">
<a-radio-group v-model="form.resource">
<a-radio value="1">
Sponsor
</a-radio>
<a-radio value="2">
Venue
</a-radio>
</a-radio-group>
</a-form-model-item>
<a-form-model-item label="Activity form">
<a-input v-model="form.desc" type="textarea" />
</a-form-model-item>
<a-form-model-item :wrapper-col="{ span: 14, offset: 4 }">
<a-button type="primary" @click="onSubmit">
Create
</a-button>
<a-button style="margin-left: 10px;">
Cancel
</a-button>
</a-form-model-item>
</a-form-model>
</template>
<script>
export default {
data() {
return {
labelCol: { span: 4 },
wrapperCol: { span: 14 },
form: {
name: '',
region: undefined,
date1: undefined,
delivery: false,
type: [],
resource: '',
desc: '',
},
};
},
methods: {
onSubmit() {
console.log('submit!', this.form);
},
},
};
</script>

@ -0,0 +1,89 @@
<template>
<div class="main" id="main">
</div>
</template>
<script>
import * as echarts from 'echarts/core';
import {
TitleComponent,
TooltipComponent,
LegendComponent
} from 'echarts/components';
import {
PieChart
} from 'echarts/charts';
import {
CanvasRenderer
} from 'echarts/renderers';
import {AnalyzeCommodity} from "../../api/inventory";
echarts.use(
[TitleComponent, TooltipComponent, LegendComponent, PieChart, CanvasRenderer]
);
export default {
data() {
return {
commodityList: []
}
},
mounted() {
AnalyzeCommodity(1).then((res) => {
if (res.status) {
let commodityList = res.data
let chartDom = document.getElementById('main');
let myChart = echarts.init(chartDom);
let option;
option = {
title: {
text: '商品入库排行分析',
left: 'center'
},
tooltip: {
trigger: 'item'
},
legend: {
orient: 'vertical',
left: 'left',
},
series: [
{
name: '访问来源',
type: 'pie',
radius: '50%',
data: commodityList,
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
};
option && myChart.setOption(option);
}
})
},
methods: {},
}
</script>
<style scoped>
.main {
width: 100%;
height: 500px;
background: #ffffff;
padding: 50px;
}
</style>

@ -0,0 +1,89 @@
<template>
<div class="main" id="main">
</div>
</template>
<script>
import * as echarts from 'echarts/core';
import {
TitleComponent,
TooltipComponent,
LegendComponent
} from 'echarts/components';
import {
PieChart
} from 'echarts/charts';
import {
CanvasRenderer
} from 'echarts/renderers';
import {AnalyzeCommodity} from "../../api/inventory";
echarts.use(
[TitleComponent, TooltipComponent, LegendComponent, PieChart, CanvasRenderer]
);
export default {
data() {
return {
commodityList: []
}
},
mounted() {
AnalyzeCommodity(-1).then((res) => {
if (res.status) {
let commodityList = res.data
let chartDom = document.getElementById('main');
let myChart = echarts.init(chartDom);
let option;
option = {
title: {
text: '商品出库排行分析',
left: 'center'
},
tooltip: {
trigger: 'item'
},
legend: {
orient: 'vertical',
left: 'left',
},
series: [
{
name: '访问来源',
type: 'pie',
radius: '50%',
data: commodityList,
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
};
option && myChart.setOption(option);
}
})
},
methods: {},
}
</script>
<style scoped>
.main {
width: 100%;
height: 500px;
background: #ffffff;
padding: 50px;
}
</style>

@ -0,0 +1,200 @@
<template>
<div>
<div style="display: flex">
<a-button size="large" class="editable-add-btn" @click="commodityVisible = true">
<a-icon type="plus"/>
新增商品
</a-button>
<a-input-search
placeholder="请输入商品名"
enter-button="搜索商品"
style="width: 400px;margin-left: 20px"
size="large"
@search="onSearch"
/>
<a-button style="margin-left: 10px" size="large" type="danger" @click="loadTableData">
重置
</a-button>
</div>
<a-table :loading="loading" :columns="columns" :data-source="data" :sortDirections="['ascend', 'descend']" rowKey="id">
<a slot="name" slot-scope="text">{{ text }}</a>
<span slot="customTitle"><a-icon type="smile-o"/> 商品名称</span>
<span slot="action" slot-scope="text, record, index">
<a-button @click="handleUpdate(record)" type="link"><a-icon type="edit"/> Update</a-button>
<a-divider type="vertical"/>
<a-button @click="handleDelete(record,index)" type="link"><a-icon type="delete"/> Delete</a-button>
</span>
</a-table>
<a-modal
title="商品信息"
:closable="false"
:visible="commodityVisible"
>
<a-form-model ref="ruleForm" :model="commodity">
<a-form-model-item ref="name" label="商品名称" prop="name">
<a-input v-model="commodity.name"/>
</a-form-model-item>
<a-form-model-item label="商品单价" prop="price">
<a-input-number id="input" v-model="commodity.price" :min="1"/>
</a-form-model-item>
<a-form-model-item label="描述信息" prop="description">
<a-input v-model="commodity.description" type="textarea"/>
</a-form-model-item>
</a-form-model>
<template slot="footer">
<a-button key="back" @click="commodityVisible = false">
Return
</a-button>
<a-button key="submit" type="primary" :loading="modalLoading" @click="submitCommodity">
Submit
</a-button>
</template>
</a-modal>
</div>
</template>
<script>
import {DeleteCommodityById, FindAllCommodity, SaveCommodity} from "@/api/commodity";
import {SearchCommodity} from "../../api/commodity";
const columns = [
{
dataIndex: 'name',
key: 'name',
slots: {title: 'customTitle'},
scopedSlots: {customRender: 'name'},
},
{
title: '库存数量',
dataIndex: 'count',
key: 'age'
// defaultSortOrder: 'descend', //
// sorter: (a, b) => { return a.count> b.count? 1 : -1 },
// sortField: 'count'
},
{
title: '描述信息',
dataIndex: 'description',
key: 'description',
},
{
title: '入库时间',
dataIndex: 'createAt',
key: 'createAt',
defaultSortOrder: 'descend', //
sorter: (a, b) => { return a.createAt> b.createAt? 1 : -1 },
sortField: 'createAt'
},
{
title: '商品单价',
key: 'price',
dataIndex: 'price',
scopedSlots: {customRender: 'tags'},
},
{
title: '更多操作',
key: 'action',
scopedSlots: {customRender: 'action'},
}
];
const data = [];
export default {
data() {
return {
commodity: {
name: '',
description: '商品简介',
count: 0,
price: 9.99,
},
loading: false,
modalLoading: false,
commodityVisible: false,
commodityLoading: false,
data: [],
columns,
};
},
mounted() {
this.loadTableData()
},
methods: {
onSearch(value) {
if (value){
this.loading = true
SearchCommodity(value).then((res) => {
console.log(res)
if (res.data.length === 0) {
this.$message.warn("未搜索到任何数据")
setTimeout(() => {
this.loading = false
this.data = res.data
}, 600)
} else {
setTimeout(() => {
this.$message.success("搜索到" + res.data.length + "个商品")
this.loading = false
this.data = res.data
}, 600)
}
})
}else {
this.$message.warn("请输入搜索内容")
}
},
loadTableData() {
this.loading = true
FindAllCommodity().then((res) => {
setTimeout(() => {
this.loading = false
this.data = res.data
}, 600)
})
},
submitCommodity() {
this.modalLoading = true
SaveCommodity(this.commodity).then((res) => {
if (res.status) {
setTimeout(() => {
this.modalLoading = false
this.commodityVisible = false
this.$message.success('商品信息提交成功');
this.loadTableData()
}, 600)
} else {
setTimeout(() => {
this.modalLoading = false
}, 600)
}
})
},
handleDelete(r, index) {
DeleteCommodityById(r.id).then((res) => {
if (res.status) this.$message.success('商品信息删除成功');
this.loadTableData()
})
console.log(index)
},
handleUpdate(r) {
this.commodity = r
this.commodityVisible = true
},
},
};
</script>
<style scoped>
.editable-add-btn {
margin-bottom: 15px;
}
</style>

@ -0,0 +1,104 @@
<template>
<div>
<a-input-search
placeholder="请输入公司名"
enter-button="搜索来往公司"
style="width: 400px;margin-bottom: 20px"
size="large"
@search="onSearch"
/>
<a-button style="margin-left: 10px" size="large" type="danger" @click="loadTableData">
重置
</a-button>
<a-table :loading="loading" :columns="columns" :data-source="data" rowKey="id">
<a slot="company" slot-scope="company">{{ company }}</a>
<span slot="customTitle"><a-icon type="bank"/> 公司名称</span>
</a-table>
</div>
</template>
<script>
import {FindAllSale, SearchCompany} from "../../api/sale";
const columns = [
{
dataIndex: 'company',
key: 'company',
slots: {title: 'customTitle'},
scopedSlots: {customRender: 'company'},
},
{
title: '预留电话',
key: 'phone',
dataIndex: 'phone',
},
{
title: '时间',
dataIndex: 'createAt',
key: 'createAt',
},
];
const data = [];
export default {
data() {
return {
loading: false,
data: [],
columns,
};
},
mounted() {
this.loadTableData()
},
methods: {
onSearch(value) {
if (value) {
this.loading = true
SearchCompany(value).then((res) => {
if (res.data.length === 0) {
this.$message.warn("未搜索到任何数据")
setTimeout(() => {
this.loading = false
this.data = res.data
}, 600)
} else {
setTimeout(() => {
this.$message.success("搜索到" + res.data.length + "个公司")
this.loading = false
this.data = res.data
}, 600)
}
})
} else {
this.$message.warn("请输入搜索内容")
}
},
loadTableData() {
this.loading = true
var that = this
console.log("loadTableData")
FindAllSale().then((res) => {
console.log("FindAllSale返回")
console.log(res)
setTimeout(() => {
this.loading = false
this.data = res.data
that.loading = false
}, 600)
})
}
},
};
</script>
<style scoped>
.editable-add-btn {
margin-bottom: 15px;
}
</style>

@ -0,0 +1,207 @@
<template>
<div class="main">
<div class="header">
<router-link to="/warehouse">
<a-icon type="arrow-left" style="padding-right: 5px"/>
返回上一页
</router-link>
</div>
<div style="display: flex">
<a-button class="editable-btn" @click="handleSubmit('in')">
入库商品
</a-button>
<a-button class="editable-btn" @click="handleSubmit('out')">
出库商品
</a-button>
<download-excel
class="export-excel-wrapper"
:data="data"
:fields="json_fields"
name="库存报表.xls">
<a-button class="editable-btn">
<a-icon type="cloud-download"/>
下载库存报表 Excel
</a-button>
</download-excel>
<a-button class="editable-btn" @click="recordVisible = true">
<a-icon type="retweet"/>
出入库记录 Excel
</a-button>
</div>
<a-table :loading="loading" :columns="columns" :data-source="data" rowKey="id">
<a slot="name" slot-scope="text">{{ text }}</a>
<span slot="customTitle"><a-icon type="smile-o"/> 商品名</span>
<span slot="count" slot-scope="count">
<a-tag color="#108ee9">{{ count }}</a-tag>
</span>
</a-table>
<a-modal
title="入库 出库"
:closable="false"
:visible="visible"
@ok="submit"
@cancel="visible = false"
>
<a-form-model :model="form">
<a-form-model-item label="请选商品">
<a-select v-model="selectIndex" placeholder="请选择入库的商品">
<a-select-option :value="index" v-for="(item, index) in commodityList" :key="index">
{{ item.name }}
</a-select-option>
</a-select>
</a-form-model-item>
<a-form-model-item label="商品数量">
<a-input-number id="inputNumber" v-model="form.count" :min="1"/>
</a-form-model-item>
<a-form-model-item label="备注">
<a-input :rows="4" v-model="form.description" type="textarea"/>
</a-form-model-item>
</a-form-model>
</a-modal>
<a-modal
title="出入库记录"
width="80%"
:visible="recordVisible"
:footer="null"
@cancel="recordVisible = false"
>
<InventoryRecords :warehouse-id="id"/>
</a-modal>
</div>
</template>
<script>
import {FindAllCommodity} from "../../api/commodity";
import {FindAllInventory, InAndOut} from "../../api/inventory";
import InventoryRecords from "../../components/InventoryRecords";
const columns = [
{
title: '商品ID',
dataIndex: 'id',
key: 'id',
width: '50%'
},
{
dataIndex: 'name',
key: 'name',
slots: {title: 'customTitle'},
scopedSlots: {customRender: 'name'},
},
{
title: '库存数量',
dataIndex: 'count',
key: 'count',
scopedSlots: {customRender: 'count'},
},
];
export default {
components: {InventoryRecords},
data() {
return {
json_fields: {
"商品ID": "cid",
"商品名称": "name",
"库存盘点数量": "count",
},
id: this.$route.params.id,
loading: false,
visible: false,
recordVisible: false,
selectIndex: 0,
commodityVisible: false,
data: [],
columns,
commodityList: [],
submitType: '',
form: {
wid: this.$route.params.id,
cid: '',
name: '',
description: '',
count: 50,
},
}
},
mounted() {
this.loadData()
FindAllCommodity().then((res) => {
if (res.status) this.commodityList = res.data
})
},
methods: {
loadData() {
this.loading = true
FindAllInventory(this.$route.params.id).then((res) => {
setTimeout(() => {
this.data = res.data
this.loading = false
}, 500)
})
},
handleSubmit(type) {
this.submitType = type
this.visible = true
},
beforeRouteEnter (to, from, next) {
// ...
},
beforeRouteLeave (to, from, next) {
// ...
},
submit() {
this.form.cid = this.commodityList[this.selectIndex].id
this.form.name = this.commodityList[this.selectIndex].name
InAndOut(this.submitType, this.form).then((res) => {
if (res.status) this.$message.success("操作成功")
this.visible = false
this.loadData()
})
},
}
}
</script>
<style scoped>
.main {
background: #ffffff;
padding: 30px;
}
.header {
font-size: 18px;
margin-bottom: 40px;
}
a {
color: #000000;
}
.header a:hover {
color: #5a84fd;
}
.editable-btn {
margin-bottom: 20px;
margin-right: 10px;
}
.in-icon {
transform: rotate(270deg);
}
</style>

@ -0,0 +1,237 @@
<template>
<div>
<a-button size="large" class="editable-add-btn" @click="visible = true">
<a-icon type="plus"/>
新增员工
</a-button>
<a-table :loading="loading" :columns="columns" :data-source="data" bordered rowKey="id">
<template
v-for="col in ['name', 'gender', 'phone','idCard','department', 'address']"
:slot="col"
slot-scope="text, record, index">
<div :key="col">
<a-input
v-if="record.editable"
style="margin: -5px 0"
:value="text"
@change="e => handleChange(e.target.value, record.id, col)"
/>
<template v-else>
{{ text }}
</template>
</div>
</template>
<template slot="operation" slot-scope="text, record, index">
<div class="editable-row-operations">
<span v-if="record.editable">
<a @click="() => save(record.id, index)">保存</a>
<a-popconfirm title="Sure to cancel?" @confirm="() => cancel(record.id)">
<a>取消</a>
</a-popconfirm>
</span>
<span v-else>
<a :disabled="editingKey !== ''" @click="() => edit(record.id)">编辑</a>
</span>
<a-popconfirm placement="top" ok-text="Yes" cancel-text="No" @confirm="confirm(record.id)">
<template slot="title">
<p> 删除驾驶员信息后将无法恢复确定要删除吗 </p>
</template>
<a-button type="link">删除</a-button>
</a-popconfirm>
</div>
</template>
</a-table>
<a-modal
title="Title"
:visible="visible"
@ok="submitForm"
@cancel="visible = false"
>
<a-form-model :model="form">
<a-form-model-item label="姓名">
<a-input v-model="form.name" placeholder="请输入司机姓名"/>
</a-form-model-item>
<a-form-model-item label="身份证号">
<a-input v-model="form.idCard" placeholder="请输入司机身份证信息"/>
</a-form-model-item>
<a-form-model-item label="联系方式">
<a-input v-model="form.phone" placeholder="请输入手机号码"/>
</a-form-model-item>
<a-form-model-item label="所在仓库">
<a-select v-model="form.department" v-for="(item,index) in warehouseList" placeholder="请选择员工所在仓库">
<a-select-option :value="item.name">{{ item.name }}</a-select-option>
</a-select>
</a-form-model-item>
<a-form-model-item label="性别">
<a-radio-group v-model="form.gender">
<a-radio value="男性">男性</a-radio>
<a-radio value="女性">女性</a-radio>
</a-radio-group>
</a-form-model-item>
<a-form-model-item label="家庭住址">
<a-input v-model="form.address" type="textarea"/>
</a-form-model-item>
</a-form-model>
</a-modal>
</div>
</template>
<script>
import {DeleteEmployeeById, FindAllEmployee, SaveEmployee} from "@/api/employee";
import {FindAllWarehouse} from "../../api/warehouse";
const columns = [
{
title: '名字',
dataIndex: 'name',
scopedSlots: {customRender: 'name'},
},
{
title: '性别',
dataIndex: 'gender',
scopedSlots: {customRender: 'gender'},
},
{
title: '所在部门',
dataIndex: 'department',
scopedSlots: {customRender: 'department'},
},
{
title: '联系电话',
dataIndex: 'phone',
scopedSlots: {customRender: 'phone'},
},
{
title: '身份证',
dataIndex: 'idCard',
scopedSlots: {customRender: 'idCard'},
},
{
title: '家庭住址',
dataIndex: 'address',
scopedSlots: {customRender: 'address'},
},
{
title: '操作',
dataIndex: 'operation',
scopedSlots: {customRender: 'operation'},
},
];
export default {
data() {
return {
loading: false,
warehouseList: [],
form: {
cacheData: [],
name: '',
gender: '男性',
phone: '',
department: '',
address: '',
idCard: '',
},
visible: false,
data: [],
columns,
editingKey: '',
};
},
mounted() {
this.loadTableData()
},
methods: {
findAllWarehouse() {
FindAllWarehouse().then((res) => {
this.warehouseList = res.data
})
},
loadTableData() {
this.loading = true
this.findAllWarehouse()
FindAllEmployee().then((res) => {
if (res.status) {
this.data = res.data
this.cacheData = res.data.map(item => ({...item}))
}
setTimeout(() => {
this.loading = false
}, 600)
})
},
submitForm() {
SaveEmployee(this.form).then((res) => {
if (res.status) this.$message.success('员工信息提交成功');
this.visible = false
this.loadTableData()
})
},
handleChange(value, id, column) {
const newData = [...this.data];
const target = newData.filter(item => id === item.id)[0];
if (target) {
target[column] = value;
this.data = newData;
}
},
edit(id) {
const newData = [...this.data];
const target = newData.filter(item => id === item.id)[0];
this.editingKey = id;
if (target) {
target.editable = true;
this.data = newData;
}
},
save(id, index) {
const newData = [...this.data];
const newCacheData = [...this.cacheData];
const target = newData.filter(item => id === item.id)[0];
const targetCache = newCacheData.filter(item => id === item.id)[0];
if (target && targetCache) {
delete target.editable;
this.data = newData;
Object.assign(targetCache, target);
this.cacheData = newCacheData;
}
this.editingKey = '';
SaveEmployee(newData[index]).then((res) => {
if (res.status) this.$message.success("信息保存成功")
})
},
cancel(id) {
const newData = [...this.data];
const target = newData.filter(item => id === item.id)[0];
this.editingKey = '';
if (target) {
Object.assign(target, this.cacheData.filter(item => id === item.id)[0]);
delete target.editable;
this.data = newData;
}
},
confirm(id) {
DeleteEmployeeById(id).then((res) => {
if (res.status) this.$message.success('Delete success');
this.loadTableData()
})
},
},
};
</script>
<style scoped>
.editable-add-btn {
margin-bottom: 15px;
}
.editable-row-operations a {
margin-right: 8px;
}
</style>

@ -0,0 +1,112 @@
<template>
<div>
<a-spin size="large" :spinning="spinning">
<a-row :gutter="20">
<a-col :span="8" class="item">
<a-card hoverable class="add-item" @click="visible = true">
<a-icon type="plus"/>
添加仓库
</a-card>
</a-col>
<a-col :span="8" v-for="(item, index) in this.data" :key="index" class="item">
<a-card hoverable>
<template slot="actions" class="ant-card-actions">
<span>负责人: {{ item.principle }}</span>
<span>
<router-link :to="'/warehouse/' + item.id">
<a-icon type="bar-chart"/> 库存管理
</router-link>
</span>
</template>
<a-card-meta
:title="item.name"
:description="'ID: ' + item.id">
<img class="image" slot="avatar" :src="imgList[Math.floor(Math.random() * 3)]"
alt=""/>
</a-card-meta>
</a-card>
</a-col>
</a-row>
</a-spin>
<a-modal
title="新增仓库"
:visible="visible"
@ok="submit"
@cancel="visible = false"
>
<a-input v-model="form.name" addon-before="仓库名称" style="width: 300px;margin-bottom: 20px"></a-input>
<a-input v-model="form.principle" addon-before="仓库负责人" style="width: 300px"></a-input>
</a-modal>
</div>
</template>
<script>
import {FindAllWarehouse, SaveWarehouse} from "@/api/warehouse";
export default {
name: "WareHouse",
data() {
return {
visible: false,
form: {id: '', principle: '', name: ''},
spinning: false,
data: [],
imgList: [
require('../../assets/warehouse0.svg'),
require('../../assets/warehouse1.svg'),
require('../../assets/warehouse2.svg'),]
}
},
mounted() {
this.loadData()
},
methods: {
loadData() {
this.spinning = true
FindAllWarehouse().then((res) => {
if (res.status) this.data = res.data
setTimeout(() => {
this.spinning = false
}, 600)
})
},
submit() {
SaveWarehouse(this.form).then((res) => {
if (res.status) this.$message.success("添加成功")
this.visible = false
this.loadData()
})
}
},
}
</script>
<style scoped>
.add-item {
text-align: center;
line-height: 120px;
min-height: 120px;
border: 1px dashed #91949c;
}
.add-item:hover {
border: 1px dashed #5a84fd;
color: #5a84fd;
}
.item {
margin-bottom: 20px;
}
.image {
width: 80px;
height: 80px;
}
</style>

@ -0,0 +1,207 @@
<template>
<div class="main">
<a-steps :current="current">
<a-step title="填写申请信息"></a-step>
<a-step title="确认配送信息"/>
<a-step title="完成申请"/>
</a-steps>
<div class="steps-content">
<div v-if="current === 0">
<a-form-model :model="form" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-form-model-item label="选择司机" required>
<a-select v-model="selectDriverIndex" placeholder="请选择配送司机">
<a-select-option :value="index" v-for="(item, index) in drivers" :key="index" :disabled="item.driving">
{{ item.name }}
<i class="dis" v-if="item.driving">
<a-icon type="close-circle"/>
正在途中</i>
</a-select-option>
</a-select>
</a-form-model-item>
<a-form-model-item label="选择运输车辆" required>
<a-select v-model="selectVehicleIndex" placeholder="请选择配送车辆">
<a-select-option :value="index" v-for="(item, index) in vehicles" :key="index" :disabled="item.driving">
{{ item.type }} : {{ item.number }}
<i class="dis" v-if="item.driving">
<a-icon type="close-circle"/>
正在途中</i>
</a-select-option>
</a-select>
</a-form-model-item>
<a-form-model-item label="预计交货时间" required>
<a-date-picker
v-model="form.time"
show-time
type="date"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
placeholder="选择日期"
style="width: 100%;"
/>
</a-form-model-item>
<a-form-model-item label="加急处理">
<a-switch v-model="form.urgent"/>
</a-form-model-item>
<a-form-model-item label="注意事项">
<a-checkbox-group v-model="form.cares">
<a-checkbox value="冰柜冷藏" name="type">
冰柜冷藏
</a-checkbox>
<a-checkbox value="注意易碎" name="type">
注意易碎
</a-checkbox>
<a-checkbox value="防止高温" name="type">
防止高温
</a-checkbox>
</a-checkbox-group>
</a-form-model-item>
<a-form-model-item label="客户电话" required>
<a-input v-model="form.phone"/>
</a-form-model-item>
<a-form-model-item label="客户地址" required>
<a-input v-model="form.address" type="textarea" :rows="4"/>
</a-form-model-item>
<a-form-model-item :wrapper-col="{ span: 14, offset: 6 }">
<a-button type="primary" @click="next">
下一步
</a-button>
</a-form-model-item>
</a-form-model>
</div>
<div v-if="current === 1" class="check">
<p>送货司机 {{ form.driver }}</p>
<p>车牌号码 {{ form.number }}</p>
<p>加急处理 {{ form.urgent }}</p>
<p>注意事项 {{ form.care }}</p>
<p>客户电话 {{ form.phone }}</p>
<p>客户地址 {{ form.address }}</p>
<p>预计送达 {{ form.time }}</p>
<a-button type="danger" style="margin-right: 20px" :loading="loading" @click="submit"></a-button>
<a-button @click="current = 0">上一步</a-button>
</div>
<div v-if="current === 2">
<a-result
status="success"
title="提交成功"
sub-title="请等待管理员查看传递请求"
>
<template #extra>
<router-link to="/delivery/list">
<a-button key="console" type="primary">
配送列表
</a-button>
</router-link>
<a-button key="buy" @click="current = 0">
再次申请
</a-button>
</template>
</a-result>
</div>
</div>
</div>
</template>
<script>
import {FindAllCanUse, SaveDistribution} from "../../api/distribution";
export default {
data() {
return {
loading: false,
labelCol: {span: 6},
wrapperCol: {span: 12},
current: 0,
selectDriverIndex: 0,
selectVehicleIndex: 0,
drivers: [],
vehicles: [],
form: {
id: '',
did: '',
vid: '',
driver: '',
number: '',
phone: '',
address: '',
urgent: false,
cares: [],
care: '',
time: '',
status: 0,
},
}
},
mounted() {
FindAllCanUse().then((res) => {
if (res.status) {
this.drivers = res.data.drivers
this.vehicles = res.data.vehicles
}
console.log(this.drivers)
console.log(this.vehicles)
})
},
methods: {
next() {
let care = ''
for (let i = 0; i < this.form.cares.length; i++) {
care += this.form.cares[i] + ", "
}
this.form.driver = this.drivers[this.selectDriverIndex].name
this.form.did = this.drivers[this.selectDriverIndex].id
this.form.number = this.vehicles[this.selectVehicleIndex].number
this.form.vid = this.vehicles[this.selectVehicleIndex].id
this.form.care = care
this.current = 1
},
submit() {
this.loading = true
SaveDistribution(this.form).then((res) => {
if (res.status) {
setTimeout(() => {
this.loading = false
this.current = 2
this.$message.success("提交成功,请等待审核")
}, 800)
} else {
setTimeout(() => {
this.loading = false
this.$message.error("提交失败")
}, 800)
}
})
},
},
}
</script>
<style scoped>
.main {
padding: 50px 180px;
background: #ffffff;
}
.steps-content {
margin-top: 40px;
}
.check {
padding-left: 200px;
}
.check p {
padding-bottom: 10px;
}
.dis {
padding-left: 10px;
font-size: 10px;
letter-spacing: 1px;
color: red;
}
</style>

@ -0,0 +1,314 @@
<template>
<div>
<a-table :loading="loading" :columns="columns" :data-source="data" bordered rowKey="id">
<span slot="status" slot-scope="status">
<a-tag v-if="status===0" color="#f50"></a-tag>
<a-tag v-if="status===1" color="#87d068"></a-tag>
<a-tag v-if="status===2" color="#2db7f5"></a-tag>
</span>
<template
v-for="col in ['phone','address']"
:slot="col"
slot-scope="text, record, index"
>
<div :key="col">
<a-input
v-if="record.editable"
style="margin: -5px 0"
:value="text"
@change="e => handleChange(e.target.value, record.id, col)"
/>
<template v-else>
{{ text }}
</template>
</div>
</template>
<template slot="operation" slot-scope="text, record, index">
<div class="editable-row-operations">
<span v-if="record.editable">
<a @click="() => save(record.id, index)">保存</a>
<a-popconfirm title="Sure to cancel?" @confirm="() => cancel(record.id)">
<a>取消</a>
</a-popconfirm>
</span>
<span v-else>
<a :disabled="editingKey !== ''" @click="() => edit(record.id)">编辑</a>
</span>
<a-button @click="review(index)" type="link" v-if="record.status===0"></a-button>
<a-button @click="review(index)" type="link" v-if="record.status===1"></a-button>
<a-button @click="review(index)" type="link" v-if="record.status===2"></a-button>
</div>
</template>
</a-table>
<a-modal
title="Title"
:visible="visible"
@ok="submitForm"
@cancel="visible = false"
>
<a-form-model :model="form">
<a-form-model-item label="姓名">
<a-input v-model="form.name" placeholder="请输入司机姓名"/>
</a-form-model-item>
<a-form-model-item label="身份证号">
<a-input v-model="form.idCard" placeholder="请输入司机身份证信息"/>
</a-form-model-item>
<a-form-model-item label="联系方式">
<a-input v-model="form.phone" placeholder="请输入手机号码"/>
</a-form-model-item>
<a-form-item label="驾照信息">
<a-row :gutter="20">
<a-col :span="12">
<a-input v-model="form.license" addon-before="" default-value="0571"/>
</a-col>
<a-col :span="7">
<a-input-number v-model="form.score" addon-before="" default-value="12" :min="0" :max="12"/>
</a-col>
</a-row>
</a-form-item>
<a-form-model-item label="性别">
<a-radio-group v-model="form.gender">
<a-radio value="男性">男性</a-radio>
<a-radio value="女性">女性</a-radio>
</a-radio-group>
</a-form-model-item>
<a-form-model-item label="家庭住址">
<a-input v-model="form.address" type="textarea"/>
</a-form-model-item>
</a-form-model>
</a-modal>
<a-modal
title="Title"
:visible="visible2"
width="60%"
:footer="null"
@cancel="visible2 = false"
>
<a-steps :current="select.status" style="padding: 50px">
<a-step title="确认信息无误"/>
<a-step title="开始配送"/>
<a-step title="配送完成"/>
</a-steps>
<div class="content">
<div v-if="select.status === 0" class="check">
<p>送货司机 {{ select.driver }}</p>
<p>车牌号码 {{ select.number }}</p>
<p>加急处理 {{ select.urgent }}</p>
<p>注意事项 {{ select.care }}</p>
<p>客户电话 {{ select.phone }}</p>
<p>客户地址 {{ select.address }}</p>
<p>预计送达 {{ select.time }}</p>
<a-button type="danger" style="margin-right: 20px" :loading="loading" @click="agree"></a-button>
<a-button @click="visible2 = false">不通过</a-button>
</div>
<div v-if="select.status === 1">
<a-result
status="success"
title="Successfully passed the audit!"
>
<template #extra>
<a-button @click="service" key="console" type="primary">
已送达目的地
</a-button>
</template>
</a-result>
</div>
<div v-if="select.status === 2">
<a-result
status="success"
title="运输订单已成功送达"
>
<template #extra>
<a-button @click="visible2 = false" key="console" type="primary">
确定
</a-button>
</template>
</a-result>
</div>
</div>
</a-modal>
</div>
</template>
<script>
import {FindAllDistribution, SaveDistribution} from "../../api/distribution";
const columns = [
{
title: '司机',
dataIndex: 'driver',
scopedSlots: {customRender: 'driver'},
},
{
title: '车牌号',
dataIndex: 'number',
scopedSlots: {customRender: 'number'},
},
{
title: '客户电话',
dataIndex: 'phone',
scopedSlots: {customRender: 'phone'},
},
{
title: '客户地址',
dataIndex: 'address',
scopedSlots: {customRender: 'address'},
},
{
title: '注意事项',
dataIndex: 'care',
scopedSlots: {customRender: 'care'},
},
{
title: '预计送达',
dataIndex: 'time',
scopedSlots: {customRender: 'time'},
},
{
title: '当前状态',
dataIndex: 'status',
scopedSlots: {customRender: 'status'},
},
{
title: '操作',
dataIndex: 'operation',
scopedSlots: {customRender: 'operation'},
},
];
export default {
data() {
return {
select: {},
loading: false,
form: {
cacheData: [],
name: '',
gender: '男性',
phone: '',
address: '',
idCard: '',
license: '',
score: 12,
},
visible: false,
visible2: false,
data: [],
columns,
editingKey: '',
};
},
mounted() {
this.loadTableData()
},
methods: {
loadTableData() {
this.loading = true
FindAllDistribution().then((res) => {
if (res.status) {
this.data = res.data
this.cacheData = res.data.map(item => ({...item}))
}
setTimeout(() => {
this.loading = false
}, 600)
})
},
submitForm() {
SaveDistribution(this.form).then((res) => {
if (res.status) this.$message.success('司机信息提交成功');
this.visible = false
this.loadTableData()
})
},
handleChange(value, id, column) {
const newData = [...this.data];
const target = newData.filter(item => id === item.id)[0];
if (target) {
target[column] = value;
this.data = newData;
}
},
edit(id) {
const newData = [...this.data];
const target = newData.filter(item => id === item.id)[0];
this.editingKey = id;
if (target) {
target.editable = true;
this.data = newData;
}
},
save(id, index) {
const newData = [...this.data];
const newCacheData = [...this.cacheData];
const target = newData.filter(item => id === item.id)[0];
const targetCache = newCacheData.filter(item => id === item.id)[0];
if (target && targetCache) {
delete target.editable;
this.data = newData;
Object.assign(targetCache, target);
this.cacheData = newCacheData;
}
this.editingKey = '';
SaveDistribution(newData[index]).then((res) => {
if (res.status) this.$message.success("信息保存成功")
})
},
cancel(id) {
const newData = [...this.data];
const target = newData.filter(item => id === item.id)[0];
this.editingKey = '';
if (target) {
Object.assign(target, this.cacheData.filter(item => id === item.id)[0]);
delete target.editable;
this.data = newData;
}
},
review(index) {
this.select = this.data[index]
this.visible2 = true
},
agree() {
this.select.status = 1
SaveDistribution(this.select)
},
service(){
this.select.status = 2
SaveDistribution(this.select)
},
},
};
</script>
<style scoped>
.editable-add-btn {
margin-bottom: 15px;
}
.editable-row-operations a {
margin-right: 8px;
}
.content {
padding: 50px 0;
}
.check {
padding-left: 200px;
}
.check p {
padding-bottom: 20px;
}
</style>

@ -0,0 +1,25 @@
<template>
<a-result status="403" title="403" sub-title="Sorry, you are not authorized to access this page.">
<template #extra>
<a-button type="primary" @click="clickBtn" style="letter-spacing: 1px">
<a-icon type="enter" />Return
</a-button>
</template>
</a-result>
</template>
<script>
export default {
data() {
return {};
},
methods: {
clickBtn() {
this.$router.go(-1)
},
}
};
</script>

@ -0,0 +1,25 @@
<template>
<a-result status="404" title="404" sub-title="Sorry, the page you visited does not exist.">
<template #extra>
<a-button type="primary" @click="clickBtn" style="letter-spacing: 1px">
<a-icon type="enter" />Return
</a-button>
</template>
</a-result>
</template>
<script>
export default {
data() {
return {};
},
methods: {
clickBtn() {
this.$router.go(-1)
},
}
};
</script>

@ -0,0 +1,25 @@
<template>
<a-result status="500" title="500" sub-title="Sorry, the server is wrong.">
<template #extra>
<a-button type="primary" @click="clickBtn" style="letter-spacing: 1px">
<a-icon type="enter" />Return
</a-button>
</template>
</a-result>
</template>
<script>
export default {
data() {
return {};
},
methods: {
clickBtn() {
this.$router.go(-1)
},
}
};
</script>

@ -0,0 +1,149 @@
<template>
<div>
<!-- <div style="display: flex">
<a-button size="large" class="editable-add-btn" @click="commodityVisible = true">
<a-icon type="plus"/>
新增商品
</a-button>
<a-input-search
placeholder="请输入商品名"
enter-button="搜索商品"
style="width: 400px;margin-left: 20px"
size="large"
@search="onSearch"
/>
<a-button style="margin-left: 10px" size="large" type="danger" @click="loadTableData">
重置
</a-button>
</div> -->
<a-table :loading="loading" :columns="columns" :data-source="data" :sortDirections="['ascend', 'descend']" rowKey="id" @change="tableChange" >
<a slot="name" slot-scope="text">{{ text }}</a>
<span slot="customTitle"><a-icon type="smile-o"/> 用户邮件</span>
<span slot="status" slot-scope="status">
<span v-show="status==1" style="color:#52c41a"></span>
<span v-show="status==0" style="color:rgb(255, 77, 79)"></span>
</span>
<span slot="action" slot-scope="text, record, index">
<a-divider type="vertical"/>
<a-button @click="handleDelete(record,index)" type="link"><a-icon type="delete"/> Delete</a-button>
</span>
</a-table>
</div>
</template>
<script>
import {FindAllLoginLog,DeleteLoginLogById} from "@/api/log";
const columns = [
{
dataIndex: 'email',
key: 'name',
slots: {title: 'customTitle'},
scopedSlots: {customRender: 'name'}
},
{
title: '浏览器类型',
dataIndex: 'browser',
key: 'description',
},
{
title: 'IP',
dataIndex: 'ip',
key: 'address',
},
{
title: '登录时间',
key: 'date',
dataIndex: 'date',
defaultSortOrder: 'descend', //
sorter: (a, b) => { return a.date> b.date? 1 : -1 },
sortField: 'date'
},
{
title: '状态',
key: 'status',
dataIndex: 'status',
scopedSlots: {customRender: 'status'},
},
{
title: '更多操作',
key: 'action',
scopedSlots: {customRender: 'action'},
},
];
const data = [];
export default {
data() {
return {
isorter:{
column:'date',
order:'desc'
},
loading: false,
modalLoading: false,
commodityVisible: false,
commodityLoading: false,
data: [],
columns,
};
},
mounted() {
this.loadTableData()
},
methods: {
//
loadTableData() {
this.loading = true
FindAllLoginLog().then(res=>{
setTimeout(() => {
this.loading = false
this.data = res.data
}, 600)
})
},
//
tableChange(pagination, filters, sorter) {
const { current, pageSize } = pagination;
// this.pagination.pageSize = pageSize;
console.log("tableChange执行了")
if (sorter.order) {
const { field, order } = sorter;
this.sort = field; //
this.sortType = order; //
} else {
console.log("不做排序");
}
this.loadTableData()
},
submitCommodity() {
this.modalLoading = true
},
//
handleDelete(r, index) {
DeleteLoginLogById(r.id).then((res) => {
if (res.status) this.$message.success('日志信息删除成功');
this.loadTableData()
})
console.log(index)
},
handleUpdate(r) {
this.commodity = r
this.commodityVisible = true
},
},
};
</script>
<style scoped>
.editable-add-btn {
margin-bottom: 15px;
}
</style>

@ -0,0 +1,160 @@
<template>
<div>
<!-- 搜索框 -->
<div style="display:flex; margin-bottom: 20px;">
<!-- 搜索表单 -->
<a-form layout="inline" style="display: flex;align-items: center;">
<a-form-item>
<a-input v-model:value="formState.account" placeholder="账号">
</a-input>
</a-form-item>
<a-form-item>
<a-input v-model:value="formState.moudle" placeholder="模块名">
</a-input>
</a-form-item>
<a-button type="primary" html-type="submit" @click="handleSubmit" >搜索</a-button>
<!-- 导出操作日志 -->
<download-excel
class="export-excel-wrapper"
style="margin-left: 20px;"
:data="data"
:fields="json_fields"
name="操作日志.xls">
<a-button class="editable-btn">
<a-icon type="cloud-download"/>
下载操作日志 Excel
</a-button>
</download-excel>
</a-form>
</div>
<!-- 表格数据 -->
<a-table :loading="loading" :columns="columns" :data-source="data"
:sortDirections="['ascend', 'descend']" rowKey="id" >
<a slot="name" slot-scope="text">{{ text }}</a>
<span slot="customTitle"><a-icon type="smile-o"/> 账号 </span>
<span slot="action" slot-scope="text, record, index">
<a-divider type="vertical"/>
<a-button @click="handleDelete(record,index)" type="link"><a-icon type="delete"/> Delete</a-button>
</span>
</a-table>
</div>
</template>
<script>
import {FindAllSystemLog,DeleteSystemLogById,querySystemLog} from "@/api/log";
const columns = [
{
dataIndex: 'account',
key: 'account',
slots: {title: 'customTitle'},
scopedSlots: {customRender: 'account'}
},
{
title: '功能模块',
dataIndex: 'module',
key: 'module',
},
{
title: '操作类型',
dataIndex: 'busincessType',
key: 'busincessType',
},
{
title: 'IP',
dataIndex: 'ip',
key: 'ip',
},
{
title: '操作类方法',
key: 'method',
dataIndex: 'method'
},
{
title: '操作时间',
key: 'time',
dataIndex: 'time',
defaultSortOrder: 'descend', //
sorter: (a, b) => { return a.time> b.time? 1 : -1 },
sortField: 'time'
},
{
title: '更多操作',
key: 'action',
scopedSlots: {customRender: 'action'},
},
];
// const data = [];
export default {
data() {
return {
formState:{
account:"",
moudle:""
},
loading: false,
modalLoading: false,
commodityVisible: false,
commodityLoading: false,
data: [],
columns,
hasSelected:false,
json_fields: {
//excel
'账号': 'account',
'模块名': 'module',
'操作类型':'busincessType',
'Ip':'IP',
'操作方法':'method',
'时间':'time'
},
};
},
mounted() {
this.loadTableData()
},
methods: {
//
loadTableData() {
this.loading = true
FindAllSystemLog().then(res=>{
setTimeout(() => {
this.loading = false
this.data = res.data
}, 600)
})
},
submitCommodity() {
this.modalLoading = true
},
//
handleDelete(r, index) {
DeleteSystemLogById(r.id).then((res) => {
if (res.status) this.$message.success('日志信息删除成功');
this.loadTableData()
})
},
//
handleSubmit(){
querySystemLog(this.formState.account,this.formState.moudle).then(res=>{
this.data = res.data
})
}
},
};
</script>
<style scoped>
.editable-add-btn {
margin-bottom: 15px;
}
</style>

@ -0,0 +1,163 @@
<template>
<div class="main">
<a-steps :current="current">
<a-step title="填写信息"></a-step>
<a-step title="确认信息"/>
<a-step title="完成"/>
</a-steps>
<div class="steps-content">
<div v-if="current === 0">
<a-form-model :model="form" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-form-model-item label="公司名称" required>
<a-input v-model="form.company"/>
</a-form-model-item>
<a-form-model-item label="打款账号" required>
<a-input v-model="form.number"/>
</a-form-model-item>
<a-form-model-item label="售出商品" required>
<a-select v-model="selectIndex" placeholder="请选择商品">
<a-select-option :value="index" v-for="(item, index) in commodityList" :key="index">
{{ item.name }}
</a-select-option>
</a-select>
</a-form-model-item>
<a-form-model-item label="商品数量" required>
<a-input-number v-model="form.count"/>
</a-form-model-item>
<a-form-model-item label="预留电话" required>
<a-input v-model="form.phone"/>
</a-form-model-item>
<a-form-model-item label="备注信息" required>
<a-input v-model="form.description" type="textarea" :rows="4"/>
</a-form-model-item>
<a-form-model-item :wrapper-col="{ span: 14, offset: 6 }">
<a-button type="primary" @click="next">
下一步
</a-button>
</a-form-model-item>
</a-form-model>
</div>
<div v-if="current === 1" class="check">
<p>收货公司 {{ form.company }}</p>
<p>打款账号 {{ form.number }}</p>
<p>售出商品 {{ form.commodity }}</p>
<p>商品数量 {{ form.count }}</p>
<p>预留电话 {{ form.phone }}</p>
<p>备注信息 {{ form.description }}</p>
<a-divider orientation="right">
金额总计 {{ form.price }}
</a-divider>
<a-button type="danger" style="margin-right: 20px" :loading="loading" @click="submit"></a-button>
<a-button @click="current = 0">上一步</a-button>
</div>
<div v-if="current === 2">
<a-result
status="success"
title="Submitted Successfully"
sub-title="Please wait for the administrator to review the delivery request."
>
<template #extra>
<router-link to="/sale/record">
<a-button key="console" type="primary">
Go Back
</a-button>
</router-link>
<a-button key="buy" @click="current = 0">
Submit Again
</a-button>
</template>
</a-result>
</div>
</div>
</div>
</template>
<script>
import {FindAllCommodity} from "../../api/commodity";
import {SaveSale} from "../../api/sale";
export default {
data() {
return {
loading: false,
labelCol: {span: 6},
wrapperCol: {span: 12},
current: 0,
selectIndex: 0,
drivers: [],
vehicles: [],
commodityList: [],
form: {
company: '',
number: '',
commodity: '',
count: 50,
price: 0,
phone: '',
description: '',
},
}
},
mounted() {
FindAllCommodity().then((res) => {
if (res.status) this.commodityList = res.data
})
},
methods: {
next() {
let commodity = this.commodityList[this.selectIndex]
this.form.price = this.form.count * commodity.price
this.form.commodity = commodity.name
console.log(this.form)
this.current = 1
},
submit() {
this.loading = true
SaveSale(this.form).then((res) => {
if (res.status) {
setTimeout(() => {
this.loading = false
this.current = 2
this.$message.success("提交成功")
}, 800)
} else {
setTimeout(() => {
this.loading = false
this.$message.error("提交失败")
}, 800)
}
})
},
},
}
</script>
<style scoped>
.main {
padding: 50px 180px;
background: #ffffff;
}
.steps-content {
margin-top: 40px;
}
.check {
padding-left: 200px;
}
.check p {
padding-bottom: 10px;
}
.dis {
padding-left: 10px;
font-size: 10px;
letter-spacing: 1px;
color: red;
}
</style>

@ -0,0 +1,122 @@
<template>
<div>
<a-table :loading="loading" :columns="columns" :sortDirections="['ascend', 'descend']" :data-source="data" rowKey="id">
<a slot="company" slot-scope="company">{{ company }}</a>
<span slot="customTitle"><a-icon type="bank" /> 公司名称</span>
<span slot="action" slot-scope="text, record, index">
<a-tag color="red" v-if="!record.pay"></a-tag>
<a-tag color="green" v-if="record.pay"></a-tag>
<a-button v-if="!record.pay" type="link" @click="confirm(record, index)"></a-button>
</span>
</a-table>
</div>
</template>
<script>
import {FindAllSale, SaveSale} from "../../api/sale";
const columns = [
{
dataIndex: 'company',
key: 'company',
slots: {title: 'customTitle'},
scopedSlots: {customRender: 'company'},
},
{
title: '打款帐号',
dataIndex: 'number',
key: 'number',
},
{
title: '商品',
dataIndex: 'commodity',
key: 'commodity',
},
{
title: '数量',
dataIndex: 'count',
key: 'count'
},
{
title: '总计',
dataIndex: 'price',
key: 'price',
},
{
title: '预留电话',
key: 'phone',
dataIndex: 'phone',
},
{
title: '备注',
dataIndex: 'description',
key: 'description',
},
{
title: '开票时间',
dataIndex: 'createAt',
key: 'createAt',
},
{
title: '更多操作',
key: 'action',
scopedSlots: {customRender: 'action'},
},
];
const data = [];
export default {
data() {
return {
loading: false,
data: [],
columns,
};
},
mounted() {
this.loadTableData()
},
methods: {
loadTableData() {
this.loading = true
FindAllSale().then((res) => {
setTimeout(() => {
this.loading = false
this.data = res.data
}, 600)
})
},
confirm(record, index) {
let that = this
this.$confirm({
title: '销售结款',
content: '我已确定' + record.company + '的销售金额 ¥' + record.price + '已经打入账户!',
okText: '确认',
cancelText: '取消',
onOk() {
that.data[index].pay = true
SaveSale(that.data[index]).then((res) => {
if (res.status) that.$message.success("销售结款成功")
})
},
});
},
},
};
</script>
<style scoped>
.editable-add-btn {
margin-bottom: 15px;
}
</style>

@ -0,0 +1,234 @@
<template>
<div>
<a-button size="large" class="editable-add-btn" @click="addVisible = true">
<a-icon type="plus"/>
添加操作员
</a-button>
<a-table :columns="columns" :data-source="admins" rowKey="id" :loading="loading">
<a slot="email" slot-scope="email">{{ email }}</a>
<span slot="customTitle"><a-icon type="smile-o"/> 邮箱</span>
<span slot="tags" slot-scope="tags">
<a-tag
v-for="tag in tags"
:key="tag"
:color="tag === 'loser' ? 'volcano' : tag.length > 5 ? 'geekblue' : 'green'"
>
{{ tag.toUpperCase() }}
</a-tag>
</span>
<span slot="action" slot-scope="text, record, index">
<a-dropdown>
<a class="ant-dropdown-link" @click="e => e.preventDefault()">操作<a-icon type="down"/></a>
<a-menu slot="overlay">
<a-menu-item>
<a-button type="link" @click="updateEmail(index)"><a-icon type="mail"/> 邮箱修改</a-button>
</a-menu-item>
<a-menu-item>
<a-button type="link" @click="updatePassword(index)"><a-icon type="lock"/> 密码修改</a-button>
</a-menu-item>
<a-menu-item>
<a-button type="link" @click="updateRoles(index)"><a-icon type="gateway"/> 权限修改</a-button>
</a-menu-item>
<a-menu-item>
<a-button type="link" @click="remove(record.id)"><a-icon type="delete"/> 删除</a-button>
</a-menu-item>
</a-menu>
</a-dropdown>
</span>
</a-table>
<a-modal
title="添加操作员"
:visible="addVisible"
@ok="submit"
@cancel="addVisible = false"
>
<a-form-model :model="selectAdmin">
<a-form-model-item label="邮箱">
<a-input v-model="selectAdmin.email"/>
</a-form-model-item>
<a-form-model-item label="密码">
<a-input v-model="selectAdmin.password"/>
</a-form-model-item>
</a-form-model>
</a-modal>
<a-modal
title="密码修改"
:visible="passwordVisible"
@ok="submit"
@cancel="passwordVisible = false"
>
<a-form-model :model="selectAdmin">
<a-form-model-item label="密码">
<a-input v-model="selectAdmin.password"/>
</a-form-model-item>
</a-form-model>
</a-modal>
<a-modal
title="邮箱修改"
:visible="emailVisible"
@ok="submit"
@cancel="emailVisible = false"
>
<a-form-model :model="selectAdmin">
<a-form-model-item label="绑定邮箱">
<a-input v-model="selectAdmin.email"/>
</a-form-model-item>
</a-form-model>
</a-modal>
<a-modal
title="权限修改"
width="500px"
:visible="rolesVisible"
@ok="submitRoles"
@cancel="rolesVisible = false"
>
<a-select
v-model="selectRoles"
mode="multiple"
style="width: 100%"
placeholder="select one country"
option-label-prop="label"
>
<a-select-option :value="item.value"
:label="item.value"
v-for="(item ,index) in roles"
:key="index">
<span role="img" :aria-label="item.value">🇨🇳</span>{{ item.value + ' (描述: ' + item.description + ')' }}
</a-select-option>
</a-select>
</a-modal>
</div>
</template>
<script>
import {DeleteAdmin, FindAllAdmin, SaveAdmin} from "../../api/admin";
import {FindAllRole} from "../../api/role";
const columns = [
{
dataIndex: 'email',
key: 'email',
slots: {title: 'customTitle'},
scopedSlots: {customRender: 'email'},
},
{
title: '密码',
dataIndex: 'password',
key: 'password',
},
{
title: '权限',
dataIndex: 'roles',
key: 'roles',
},
{
title: '添加日期',
key: 'createAt',
dataIndex: 'createAt',
scopedSlots: {customRender: 'createAt'},
},
{
title: '操作',
key: 'action',
scopedSlots: {customRender: 'action'},
},
];
export default {
data() {
return {
roles: [],
selectRoles: [],
admins: [],
loading: false,
addVisible: false,
emailVisible: false,
passwordVisible: false,
rolesVisible: false,
columns,
selectAdmin: {},
};
},
mounted() {
this.load()
},
methods: {
load() {
this.loading = true
FindAllRole().then((res) => {
if (res.status) this.roles = res.data
})
FindAllAdmin().then((res) => {
setTimeout(() => {
this.admins = res.data
this.loading = false
}, 600)
})
},
submit() {
SaveAdmin(this.selectAdmin).then((res) => {
if (res.status) this.$message.success("操作员信息更新成功")
this.addVisible = false
this.emailVisible = false
this.rolesVisible = false
this.passwordVisible = false
this.load()
})
},
submitRoles() {
let str = ''
for (let i = 0; i < this.selectRoles.length; i++) {
if (i === this.selectRoles.length - 1) {
str += this.selectRoles[i]
} else {
str += this.selectRoles[i] + ";"
}
}
this.selectAdmin.roles = str
this.submit()
},
updateEmail(index) {
this.emailVisible = true
this.selectAdmin = this.admins[index]
},
updatePassword(index) {
this.passwordVisible = true
this.selectAdmin = this.admins[index]
},
updateRoles(index) {
this.rolesVisible = true
this.selectAdmin = this.admins[index]
this.selectRoles = this.selectAdmin.roles.split(";")
console.log(this.selectRoles)
},
remove(id) {
DeleteAdmin(id).then((res) => {
if (res.status) this.$message.success("操作员删除成功")
this.load()
})
},
},
};
</script>
<style scoped>
.editable-add-btn {
margin-bottom: 15px;
}
</style>

@ -0,0 +1,71 @@
<template>
<div class="main">
<div class="title">系统权限列表</div>
<a-spin size="large" :spinning="spinning">
<div v-for="(item, index) in roles" :key="index">
<a class="item-title" type="link">{{ item.value }}</a>
<p class="des">描述信息 {{ item.description }}</p>
<a-divider/>
</div>
</a-spin>
</div>
</template>
<script>
import {FindAllRole} from "../../api/role";
export default {
name: "Me",
data() {
return {
roles: [],
spinning: false,
}
},
mounted() {
this.spinning = true
FindAllRole().then((res) => {
if (res.status) this.roles = res.data
setTimeout(() => {
this.spinning = false
}, 500)
})
},
}
</script>
<style scoped>
.main {
background: #ffffff;
padding: 50px 70px;
}
.title {
letter-spacing: 1px;
font-size: 20px;
font-weight: bolder;
color: #000000;
margin-bottom: 30px;
}
.btn {
float: right;
}
.item-title {
color: rgba(0, 0, 0, .8);
line-height: 35px;
}
.item-title:hover {
color: #5a84fd;
}
.des {
color: rgba(0, 0, 0, .5);
}
</style>

@ -0,0 +1,127 @@
<template>
<div class="main">
<div class="title">安全设置</div>
<a-spin size="large" :spinning="spinning">
<div>
<a class="item-title" type="link">账号密码</a>
<a-button class="btn" @click="passwordVisible = true" type="link">修改</a-button>
<p class="des">账号密码 {{ show ? this.$store.state.user.details.password : '********' }}
<a @click="show = !show" style="padding-left: 10px">
<a-icon :type="!show ? 'eye' : 'eye-invisible' "/>
</a>
</p>
<a-divider/>
</div>
<div>
<a class="item-title" type="link">绑定邮箱</a>
<a-button class="btn" @click="emailVisible = true" type="link">修改</a-button>
<p class="des">已绑定邮箱 {{ this.$store.state.user.details.email }}</p>
<a-divider/>
</div>
</a-spin>
<a-modal
title="密码修改"
:visible="passwordVisible"
@ok="submit"
@cancel="passwordVisible = false"
>
<a-form-model :model="admin">
<a-form-model-item label="密码">
<a-input v-model="admin.password"/>
</a-form-model-item>
</a-form-model>
</a-modal>
<a-modal
title="邮箱修改"
:visible="emailVisible"
@ok="submit"
@cancel="emailVisible = false"
>
<a-form-model :model="admin">
<a-form-model-item label="绑定邮箱">
<a-input v-model="admin.email"/>
</a-form-model-item>
</a-form-model>
</a-modal>
</div>
</template>
<script>
import {SaveAdmin} from "@/api/admin";
export default {
name: "Me",
data() {
return {
admin: this.$store.state.user.details,
show: false,
emailVisible: false,
passwordVisible: false,
spinning: false,
}
},
mounted() {
this.load()
},
methods: {
load() {
this.spinning = true
setTimeout(() => {
this.spinning = false
}, 500)
},
submit() {
SaveAdmin(this.admin).then((res) => {
if (res.status) {
this.$message.success("账号信息修改成功")
this.$store.commit('user/saveLoginUser', res.data)
}
})
this.emailVisible = false
this.passwordVisible = false
this.load()
},
}
}
</script>
<style scoped>
.main {
background: #ffffff;
padding: 50px 70px;
}
.title {
letter-spacing: 1px;
font-size: 20px;
font-weight: bolder;
color: #000000;
margin-bottom: 30px;
}
.btn {
float: right;
}
.item-title {
color: rgba(0, 0, 0, .8);
line-height: 35px;
}
.item-title:hover {
color: #5a84fd;
}
.des {
color: rgba(0, 0, 0, .5);
}
</style>

@ -0,0 +1,237 @@
<template>
<div>
<a-button size="large" class="editable-add-btn" @click="visible = true">
<a-icon type="plus"/>
新增驾驶员
</a-button>
<a-table :loading="loading" :columns="columns" :data-source="data" bordered rowKey="id">
<template
v-for="col in ['name', 'gender', 'phone','score','idCard','address', 'license']"
:slot="col"
slot-scope="text, record, index"
>
<div :key="col">
<a-input
v-if="record.editable"
style="margin: -5px 0"
:value="text"
@change="e => handleChange(e.target.value, record.id, col)"
/>
<template v-else>
{{ text }}
</template>
</div>
</template>
<template slot="operation" slot-scope="text, record, index">
<div class="editable-row-operations">
<span v-if="record.editable">
<a @click="() => save(record.id, index)">保存</a>
<a-popconfirm title="Sure to cancel?" @confirm="() => cancel(record.id)">
<a>取消</a>
</a-popconfirm>
</span>
<span v-else>
<a :disabled="editingKey !== ''" @click="() => edit(record.id)">编辑</a>
</span>
<a-popconfirm placement="top" ok-text="Yes" cancel-text="No" @confirm="confirm(record.id)">
<template slot="title">
<p> 删除驾驶员信息后将无法恢复确定要删除吗 </p>
</template>
<a-button type="link">删除</a-button>
</a-popconfirm>
</div>
</template>
</a-table>
<a-modal
title="Title"
:visible="visible"
@ok="submitForm"
@cancel="visible = false"
>
<a-form-model :model="form">
<a-form-model-item label="姓名">
<a-input v-model="form.name" placeholder="请输入司机姓名"/>
</a-form-model-item>
<a-form-model-item label="身份证号">
<a-input v-model="form.idCard" placeholder="请输入司机身份证信息"/>
</a-form-model-item>
<a-form-model-item label="联系方式">
<a-input v-model="form.phone" placeholder="请输入手机号码"/>
</a-form-model-item>
<a-form-item label="驾照信息">
<a-row :gutter="20">
<a-col :span="12">
<a-input v-model="form.license" addon-before="" default-value="0571"/>
</a-col>
<a-col :span="7">
<a-input-number v-model="form.score" addon-before="" default-value="12" :min="0" :max="12"/>
</a-col>
</a-row>
</a-form-item>
<a-form-model-item label="性别">
<a-radio-group v-model="form.gender">
<a-radio value="男性">男性</a-radio>
<a-radio value="女性">女性</a-radio>
</a-radio-group>
</a-form-model-item>
<a-form-model-item label="家庭住址">
<a-input v-model="form.address" type="textarea"/>
</a-form-model-item>
</a-form-model>
</a-modal>
</div>
</template>
<script>
import {DeleteDriverById, FindAllDriver, SaveDriver} from "@/api/driver";
const columns = [
{
title: '名字',
dataIndex: 'name',
scopedSlots: {customRender: 'name'},
},
{
title: '性别',
dataIndex: 'gender',
scopedSlots: {customRender: 'gender'},
},
{
title: '联系电话',
dataIndex: 'phone',
scopedSlots: {customRender: 'phone'},
},
{
title: '驾驶证',
dataIndex: 'license',
scopedSlots: {customRender: 'license'},
},
{
title: '驾证分数',
dataIndex: 'score',
scopedSlots: {customRender: 'score'},
},
{
title: '身份证',
dataIndex: 'idCard',
scopedSlots: {customRender: 'idCard'},
},
{
title: '家庭住址',
dataIndex: 'address',
scopedSlots: {customRender: 'address'},
},
{
title: '操作',
dataIndex: 'operation',
scopedSlots: {customRender: 'operation'},
},
];
export default {
data() {
return {
loading: false,
form: {
cacheData: [],
name: '',
gender: '男性',
phone: '',
address: '',
idCard: '',
license: '',
score: 12,
},
visible: false,
data: [],
columns,
editingKey: '',
};
},
mounted() {
this.loadTableData()
},
methods: {
loadTableData() {
this.loading = true
FindAllDriver().then((res) => {
if (res.status) {
this.data = res.data
this.cacheData = res.data.map(item => ({...item}))
}
setTimeout(() => {
this.loading = false
}, 600)
})
},
submitForm() {
SaveDriver(this.form).then((res) => {
if (res.status) this.$message.success('司机信息提交成功');
this.visible = false
this.loadTableData()
})
},
handleChange(value, id, column) {
const newData = [...this.data];
const target = newData.filter(item => id === item.id)[0];
if (target) {
target[column] = value;
this.data = newData;
}
},
edit(id) {
const newData = [...this.data];
const target = newData.filter(item => id === item.id)[0];
this.editingKey = id;
if (target) {
target.editable = true;
this.data = newData;
}
},
save(id, index) {
const newData = [...this.data];
const newCacheData = [...this.cacheData];
const target = newData.filter(item => id === item.id)[0];
const targetCache = newCacheData.filter(item => id === item.id)[0];
if (target && targetCache) {
delete target.editable;
this.data = newData;
Object.assign(targetCache, target);
this.cacheData = newCacheData;
}
this.editingKey = '';
SaveDriver(newData[index]).then((res) => {
if (res.status) this.$message.success("信息保存成功")
})
},
cancel(id) {
const newData = [...this.data];
const target = newData.filter(item => id === item.id)[0];
this.editingKey = '';
if (target) {
Object.assign(target, this.cacheData.filter(item => id === item.id)[0]);
delete target.editable;
this.data = newData;
}
},
confirm(id) {
DeleteDriverById(id).then((res) => {
if (res.status) this.$message.success('Delete success');
this.loadTableData()
})
},
},
};
</script>
<style scoped>
.editable-add-btn {
margin-bottom: 15px;
}
.editable-row-operations a {
margin-right: 8px;
}
</style>

@ -0,0 +1,127 @@
<template>
<div>
<a-spin size="large" :spinning="spinning">
<a-row :gutter="20">
<a-col :span="8" class="item">
<a-card hoverable class="add-item" @click="visible = true">
<a-icon type="plus"/>
添加车辆
</a-card>
</a-col>
<a-col :span="8" v-for="(item, index) in this.data" :key="index" class="item">
<a-card hoverable>
<!-- <template slot="actions" class="ant-card-actions">-->
<!-- <a-tag :color=" item.driving ? 'orange': 'green'">{{ item.driving ? '正在途中' : '正在休息' }}</a-tag>-->
<!-- <span>使用记录</span>-->
<!-- </template>-->
<a-card-meta
:title="'车牌号:' + item.number"
:description="'ID: ' + item.id">
<a-badge :number-style="{ backgroundColor: '#52c41a' }"
slot="avatar"
:count="item.type"
:offset="[-80,10]">
<img class="image" :src="require('../../assets/' +item.type+'.svg')" alt=""/>
</a-badge>
</a-card-meta>
</a-card>
</a-col>
</a-row>
</a-spin>
<a-modal
title="新增仓库"
:visible="visible"
@ok="submit"
@cancel="visible = false"
>
<a-form-model :model="form">
<a-form-model-item label="车牌号码">
<a-input v-model="form.number"/>
</a-form-model-item>
<a-form-model-item label="车辆类型">
<a-select v-model="form.type">
<a-select-option value="小型汽车">小型汽车</a-select-option>
<a-select-option value="货车">货车</a-select-option>
<a-select-option value="卡车">卡车</a-select-option>
</a-select>
</a-form-model-item>
</a-form-model>
</a-modal>
</div>
</template>
<script>
import {FindAllVehicle, SaveVehicle} from "@/api/vehicle";
export default {
name: "WareHouse",
data() {
return {
visible: false,
spinning: false,
form: {
number: '京A0000',
type: '货车',
driving: false,
},
data: [],
imgList: [
require('../../assets/warehouse0.svg'),
require('../../assets/warehouse1.svg'),
require('../../assets/warehouse2.svg'),]
}
},
mounted() {
this.loadData()
},
methods: {
loadData() {
this.spinning = true
FindAllVehicle().then((res) => {
if (res.status) this.data = res.data
setTimeout(() => {
this.spinning = false
}, 600)
})
},
submit() {
SaveVehicle(this.form).then((res) => {
if (res.status) this.$message.success("添加成功")
this.visible = false
this.loadData()
})
}
},
}
</script>
<style scoped>
.add-item {
text-align: center;
line-height: 120px;
min-height: 120px;
border: 1px dashed #91949c;
}
.add-item:hover {
border: 1px dashed #5a84fd;
color: #5a84fd;
}
.item {
margin-bottom: 20px;
}
.image {
width: 80px;
height: 80px;
margin-left: 20px;
}
</style>
Loading…
Cancel
Save