first commit

main
luth1ng 8 months ago
parent 47971324a3
commit ae3a0dcab8

@ -0,0 +1,23 @@
.DS_Store
node_modules
dist/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
*.log
tests/**/coverage/
tests/e2e/reports
selenium-debug.log
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.local
package-lock.json
yarn.lock

@ -0,0 +1,70 @@
# 飞龙快递系统
## 系统概述
- 基于vue3框架开发
- 用例图如下:
![alt text](image.png)
![alt text](image-1.png)
## 代码书写说明
`以下是 Vue 项目 src 目录的详细说明`
```
src/
├── assets/ # 静态资源
│ ├── css/ # 样式文件
│ │ └── base.css # 基础样式文件
│ └── (各种图片) # 图片资源文件夹
├── components/ # 可复用的 Vue 组件
│ ├── Carousel.vue # 轮播组件
│ ├── Header.vue # 页面头部组件
│ ├── Footer.vue # 页脚底部组件
│ └── Login.vue # 登录注册组件
├── hooks/ # 自定义 Hooks
│ └── useEmitter.js # 事件发射器 Hook
├── icons/ # 图标资源
│ └── svg/ # SVG 图标文件夹
│ └── (各种图标) # 各类 SVG 图标
├── router/ # 路由配置
│ └── index.js # 主路由配置文件
├── store/ # Vuex 状态管理
│ └── index.js # Vuex Store 入口文件
├── views/ # 页面视图组件
│ ├── back/ # 后台管理页面
| | ├── index.vue # 后台管中心
| | ├── order.vue # 订单管理中心
| | ├── personal.vue # 人员管理中心
| | ├── problem.vue # 问题管理中心
| | └── backlogin.vue # 后台登录
│ ├── courier/ # 快递员页面
│ │ ├── index.vue # 快递员中心
│ │ └── order.vue # 订单管理中心
│ ├── fore/ # 前台用户页面
│ │ ├── check.vue #查件
│ │ ├── delivery.vue # 寄件
│ │ ├── service.vue # 快递服务
│ │ ├── profile.vue # 我的资料
| | ├── address.vue # 地址簿
│ │ ├── complaint.vue # 快递投诉
│ | └── feedback.vue # 我有建议
│ └── home.vue # 主页面组件
├── App.vue # 主应用组件
├── main.js # 应用入口文件
└── request/ # 请求相关文件夹
└── style.css # 请求样式文件
```
### 目录及文件描述
- `assets/:` 存放静态资源,如 CSS 文件、图片等。
- `components/:` 存放可复用的 Vue 组件,组织项目中的 UI 元素。
- `hooks/:` 自定义 Hook包含复用的逻辑。
- `icons/:` 存放 SVG 图标文件。
- `router/:` 路由配置文件,管理应用的路由和导航。
- `store/:` Vuex 状态管理相关文件,维护全局状态。
- `views/:` 页面视图组件,组织应用的主要页面内容。
- `App.vue:` Vue 应用的根组件。
- `main.js:` 应用的入口文件,初始化 Vue 实例和配置。
- `request/:` 存放与网络请求相关的文件。

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/png" href="/hear.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>快递系统</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

@ -0,0 +1,24 @@
{
"name": "practical_project",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"axios": "^1.7.7",
"element-china-area-data": "^6.1.0",
"element-plus": "^2.8.7",
"mitt": "^3.0.1",
"pinia": "^2.2.5",
"vue": "^3.5.10",
"vue-router": "4"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.1.4",
"vite": "^5.4.8"
}
}

@ -0,0 +1,10 @@
<template>
<router-view />
</template>
<script setup>
</script>
<style>
@import url("./assets/css/base.css");
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 419 B

@ -0,0 +1,44 @@
.content {
width: 100%;
height: 100%;
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
}
/* 水平布局 居中*/
.horizontalView {
position: relative;
flex-direction: row;
display: flex;
align-items: center;
}
/* 垂直布局居中 */
.verticalView {
position: relative;
flex-direction: column;
display: flex;
align-items: center;
}
/* 居中 */
.center {
position: absolute;
top: 50%;
left: 50%;
font-size: 28px;
transform: translate(-50%, -50%);
}
.w100 {
width: 100%;
}
.h100 {
height: 100%;
}
.icon-svg {
width: 1.4rem;
height: 1.4rem;
fill: currentColor;
overflow: hidden;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 490 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

@ -0,0 +1,68 @@
<template>
<el-carousel :interval="4000" type="card" height="400px">
<el-carousel-item v-for="item in carouseData" :key="item.url">
<img :src="item.url" alt="" @error="handleImageError($event, item.url)" />
</el-carousel-item>
</el-carousel>
</template>
<script setup>
import { onMounted, ref } from 'vue';
const carouseData = ref([]);
const initializeCarouselData = () => {
const urls = [
"../assets/sf1.jpg",
"../assets/sf2.jpg",
"../assets/sf3.png",
"../assets/sf4.jpg",
"../assets/sf5.jpg",
"../assets/sf6.jpg",
"../assets/sf7.jpg",
"../assets/sf8.jpg",
];
carouseData.value = urls.map(url => ({
url: new URL(url, import.meta.url).href
}));
};
onMounted(() => {
initializeCarouselData();
});
const handleImageError = (event, url) => {
console.error(`Failed to load image: ${url}`);
event.target.src = '/path/to/default/image.jpg';
};
</script>
<style scoped>
.el-carousel__item h3 {
color: #475669;
opacity: 0.75;
line-height: 285px;
margin: 0;
text-align: center;
}
img {
width: 100%; /* 设置图片宽度为容器的100% */
height: 100%; /* 设置图片高度为容器的100% */
object-fit: cover; /* 保持图片的宽高比 */
}
.el-carousel__item {
text-align: center; /* 确保图片在容器中居中 */
overflow: hidden; /* 如果图片超出容器大小,隐藏超出部分 */
height: 400px; /* 设置容器高度与轮播图高度一致 */
}
.el-carousel__item:nth-child(2n) {
background-color: #99a9bf;
}
.el-carousel__item:nth-child(2n + 1) {
background-color: #d3dce6;
}
</style>

@ -0,0 +1,85 @@
<template>
<!-- 网页底部 -->
<footer class="footer">
<div class="container">
<p>版权所有 © 2024 FlieslongExpress团队 保留所有权利</p>
<nav>
<ul>
<li><el-button :type="'primary'" text class="btn btn-link" @click="showAbout"></el-button></li>
<li><el-button :type="'primary'" text class="btn btn-link" @click="showContact"></el-button></li>
</ul>
</nav>
<!-- 回到顶部按钮 -->
<el-backtop :right="100" :bottom="100" />
</div>
<!--关于我们的弹窗-->
<el-dialog title="关于我们" v-model="aboutDialogVisible" width="600px" align-center>
<el-text>
<p>FlieslongExpress团队</p>
<p>旨在通过网络实现快递的在线服务</p>
<p>此项目基于Vue3+element-plus开发完成</p>
</el-text>
</el-dialog>
<!--联系我们的弹窗-->
<el-dialog title="联系我们" v-model="contactDialogVisible" width="600px" align-center>
<el-text>
<p>电话123456789</p>
<p>邮箱123456789@qq.com</p>
<p>地址中国</p>
<img style="width: 100px; height: 100px" src='../assets/about.png' />
</el-text>
</el-dialog>
</footer>
</template>
<script setup>
import { ref } from 'vue'
const aboutDialogVisible = ref(false)
const contactDialogVisible = ref(false)
function showAbout() {
aboutDialogVisible.value = true
}
function showContact() {
contactDialogVisible.value = true
}
</script>
<style>
.footer {
background-color: #f8f9fa;
padding: 20px 0;
text-align: center;
}
.footer .container {
max-width: 1200px;
margin: 0 auto;
}
.footer p {
color: #6c757d;
font-size: 14px;
}
.footer nav ul {
list-style: none;
padding: 0;
margin: 10px 0 0;
}
.footer nav ul li {
display: inline;
margin: 0 10px;
}
.footer nav ul li a {
color: #007bff;
text-decoration: none;
}
.footer nav ul li a:hover {
text-decoration: underline;
}
</style>

@ -0,0 +1,128 @@
<template>
<div class="app-container">
<div class="header">
<h1 class="title">快递系统</h1>
</div>
<div class="menu">
<el-menu :default-active="activeIndex" :router="true" class="el-menu-demo" mode="horizontal" :ellipsis="false"
@select="handleSelect">
<el-menu-item index="0" :route="{ name: 'home' }">首页</el-menu-item>
<el-menu-item index="1" :route="{ name: 'delivery' }">寄件</el-menu-item>
<el-menu-item index="2" :route="{ name: 'check' }">查件</el-menu-item>
<el-menu-item index="3" :route="{ name: 'service' }">服务查询</el-menu-item>
<el-sub-menu index="4">
<template #title>问题反馈</template>
<el-menu-item index="4-1" :route="{ name: 'feedback' }">我有建议</el-menu-item>
<el-menu-item index="4-2" :route="{ name: 'complaint' }">快递投诉</el-menu-item>
</el-sub-menu>
<el-sub-menu index="5" class="menu-item-right">
<template #title>个人中心</template>
<el-menu-item index="5-1" :route="{ name: 'profile' }">我的资料</el-menu-item>
<el-menu-item index="5-2" :route="{ name: 'address' }">地址簿</el-menu-item>
</el-sub-menu>
</el-menu>
</div>
<div class="content">
<router-view></router-view>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, watchEffect } from 'vue';
import { useRoute } from 'vue-router';
//
const activeIndex = ref('0');
//
const route = useRoute();
//
watchEffect(() => {
switch (route.name) {
case 'home':
activeIndex.value = '0';
break;
case 'delivery':
activeIndex.value = '1';
break;
case 'check':
activeIndex.value = '2';
break;
case 'service':
activeIndex.value = '3';
break;
case 'feedback':
activeIndex.value = '4-1';
break;
case 'complaint':
activeIndex.value = '4-2';
break;
case 'profile':
activeIndex.value = '5-1';
break;
case 'address':
activeIndex.value = '5-2';
break;
default:
activeIndex.value = '0';
break;
}
});
//
const handleSelect = (index: string) => {
// console.log(':', index);
};
</script>
<style scoped>
.app-container {
display: relative;
flex-direction: column;
height: 100vh;
}
.header {
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index: 1000;
background-color: #a4bacb;
color: aliceblue;
padding: 10px 20px;
}
.menu {
margin-top: 50px;
}
.title {
font-size: 15px;
text-align: center;
}
.content {
margin-top: 100px;
/* 根据 header 和 menu 的高度调整 */
flex: 1;
overflow-y: auto;
}
@media (max-width: 600px) {
.header h1 {
font-size: 1.5rem;
}
.el-menu--horizontal {
font-size: 0.9rem;
}
}
.menu-item-right {
margin-left: auto;
}
</style>

@ -0,0 +1,493 @@
<template>
<div class="body">
<div class="main">
<div class="switch" id="switch-cnt">
<div class="switch__circle"></div>
<div class="switch__circle switch__circle--t"></div>
<div class="switch__container" id="switch-c1">
<h2 class="switch__title title">欢迎!</h2>
<p class="switch__description description">已有帐号现在登陆</p>
<button class="switch__button button switch-btn" @click="change"></button>
<button class="switch__button button switch-btn" @click="toBack"></button>
</div>
<div class="switch__container is-hidden" id="switch-c2">
<h2 class="switch__title title">您好</h2>
<p class="switch__description description">没有账号现在注册</p>
<button class="switch__button button switch-btn" @click="change"></button>
</div>
</div>
<!-- signup-->
<div class="container a-container" id="a-container">
<el-form class="form" id="a-form" method="" action="">
<h2 class="form_title title">注册</h2>
<el-input class="form__input" type="text" placeholder="姓名" v-model="registerForm.name" />
<br>
<el-input class="form__input" type="text" placeholder="邮箱" v-model="registerForm.email" />
<br>
<el-input class="form__input" type="password" placeholder="密码" v-model="registerForm.password" />
<button class="form__button button submit" @click="register"></button>
</el-form>
</div>
<!-- signin-->
<div class="container b-container" id="b-container">
<el-form :model="foreuser" :rules="rules" ref="ruleFormRef" class="form" id="b-form" method=""
action="#">
<h2 class="form_title title">登陆</h2>
<el-form-item prop="mobile">
<el-input class="form__input" type="text" placeholder="电话号码" v-model="foreuser.mobile" />
</el-form-item>
<br>
<el-form-item prop="password">
<el-input class="form__input" type="password" placeholder="密码" v-model="foreuser.password"
show-password />
</el-form-item>
<el-button link class="form__link" @click="rescue">?</el-button>
<el-form-item>
<el-button class="form__button button submit" @click="login"></el-button>
</el-form-item>
</el-form>
</div>
</div>
</div>
</template>
<script setup>
import { reactive, onMounted, ref } from 'vue';
import { useRouter } from 'vue-router';
import { ElMessage } from 'element-plus';
import useEmitter from '../hooks/useEmitter.js'
//
const router = useRouter();
//
const foreuser = reactive({
mobile: '',
password: '',
});
//
const registerForm = reactive({
name: '',
email: '',
password: '',
});
//
const rules = reactive({
mobile: [
{ required: true, message: '请输入手机号', trigger: 'blur' },
{
validator: (rule, value, callback) => {
if (!value || value.trim() === '') {
callback(new Error('手机号不能为空'));
} else if (!/^((13[0-9]|15[0-9]|16[0-9]|17[3-8]|18[0-9]|19[0-9]|14[5-7])\d{8})$/.test(value)) {
callback(new Error('请输入正确的手机号格式'));
} else {
callback();
}
}, trigger: 'blur'
}
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
],
email: [
{ required: true, message: '请输入邮箱', trigger: 'blur' },
{
validator: (rule, value, callback) => {
if (!value || value.trim() === '') {
callback(new Error('邮箱不能为空'));
} else if (!/^[0-9a-zA-Z_.-]+@[0-9a-zA-Z_.-]+\.[a-zA-Z]{1,2}$/.test(value)) {
callback(new Error('请输入正确的邮箱格式'));
} else {
callback();
}
}, trigger: 'blur'
}
]
});
//
const toBack = () => {
router.push({ name: 'backLogin' });
};
const rescue = () => {
router.push({ name: 'rescue' });
};
//
const users = [
{ mobile: '18960955706', password: '123456' },
];
//
const login = () => {
const user = users.find(user => user.mobile === foreuser.mobile && user.password === foreuser.password);
if (user) {
ElMessage({
message: '登陆成功!',
type: 'success'
});
router.push({ name: 'home' });
} else {
ElMessage({
message: '用户名或密码错误!',
type: 'error'
});
}
};
const register = () => {
//
if (registerForm.name && registerForm.email && registerForm.password) {
ElMessage({
message: '注册成功!',
type: 'success'
});
//
router.push({ name: 'Header' });
} else {
ElMessage({
message: '请填写完整信息!',
type: 'error'
});
}
};
//
const sidebarOpen = ref(true)
const emitter = useEmitter()
const change = () => {
const switchC1 = document.querySelector("#switch-c1");
const switchC2 = document.querySelector("#switch-c2");
const switchCircle = document.querySelectorAll(".switch__circle");
const switchCtn = document.querySelector("#switch-cnt");
switchCtn.classList.add("is-gx");
setTimeout(() => {
switchCtn.classList.remove("is-gx");
}, 1500);
switchCtn.classList.toggle("is-txr");
switchCircle[0].classList.toggle("is-txr");
switchCircle[1].classList.toggle("is-txr");
//
switchC1.classList.toggle("is-hidden");
switchC2.classList.toggle("is-hidden");
// 使
sidebarOpen.value = !sidebarOpen.value;
emitter.emit('change', sidebarOpen.value);
};
onMounted(() => {
emitter.on('change', (isOpen) => {
const aContainer = document.querySelector("#a-container");
const bContainer = document.querySelector("#b-container");
//
aContainer.classList.toggle("is-txl");
bContainer.classList.toggle("is-txl");
bContainer.classList.toggle("is-z200");
});
});
</script>
<style scoped>
*,
*::after,
*::before {
margin: 0;
padding: 0;
box-sizing: border-box;
user-select: none;
}
/* Generic */
.body {
width: 100%;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
font-family: 'Montserrat', sans-serif;
font-size: 12px;
background-color: #ecf0f3;
color: #a0a5a8;
}
/**/
.main {
position: relative;
width: 1000px;
min-width: 1000px;
min-height: 600px;
height: 600px;
padding: 25px;
background-color: #ecf0f3;
box-shadow: 10px 10px 10px #d1d9e6, -10px -10px 10px #f9f9f9;
border-radius: 12px;
overflow: hidden;
}
@media (max-width: 1200px) {
.main {
transform: scale(0.7);
}
}
@media (max-width: 1000px) {
.main {
transform: scale(0.6);
}
}
@media (max-width: 800px) {
.main {
transform: scale(0.5);
}
}
@media (max-width: 600px) {
.main {
transform: scale(0.4);
}
}
.container {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 0;
width: 600px;
height: 100%;
padding: 25px;
background-color: #ecf0f3;
transition: 1.25s;
}
.form {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
width: 70%;
height: 100%;
}
.form__icon {
object-fit: contain;
width: 30px;
margin: 0 5px;
opacity: .5;
transition: .15s;
}
.form__icon:hover {
opacity: 1;
transition: .15s;
cursor: pointer;
}
.form__span {
margin-top: 30px;
margin-bottom: 12px;
}
.form__link {
color: #181818;
font-size: 15px;
margin-top: 25px;
border-bottom: 1px solid #a0a5a8;
line-height: 2;
}
.title {
font-size: 34px;
font-weight: 700;
line-height: 3;
color: #181818;
}
.description {
font-size: 14px;
letter-spacing: .25px;
text-align: center;
line-height: 1.6;
}
.button {
width: 180px;
height: 50px;
border-radius: 25px;
margin-top: 50px;
font-weight: 700;
font-size: 14px;
letter-spacing: 1.15px;
background-color: #4B70E2;
color: #f9f9f9;
box-shadow: 8px 8px 16px #d1d9e6, -8px -8px 16px #f9f9f9;
border: none;
outline: none;
}
/**/
.a-container {
z-index: 100;
left: calc(100% - 600px);
}
.b-container {
left: calc(100% - 600px);
z-index: 0;
}
.switch {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 400px;
padding: 50px;
z-index: 200;
transition: 1.25s;
background-color: #ecf0f3;
overflow: hidden;
box-shadow: 4px 4px 10px #d1d9e6, -4px -4px 10px #f9f9f9;
}
.switch__circle {
position: absolute;
width: 500px;
height: 500px;
border-radius: 50%;
background-color: #ecf0f3;
box-shadow: inset 8px 8px 12px #d1d9e6, inset -8px -8px 12px #f9f9f9;
bottom: -60%;
left: -60%;
transition: 1.25s;
}
.switch__circle--t {
top: -30%;
left: 60%;
width: 300px;
height: 300px;
}
.switch__container {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
position: absolute;
width: 400px;
padding: 50px 55px;
transition: 1.25s;
}
.switch__button {
cursor: pointer;
}
.switch__button:hover {
box-shadow: 6px 6px 10px #d1d9e6, -6px -6px 10px #f9f9f9;
transform: scale(0.985);
transition: .25s;
}
.switch__button:active,
.switch__button:focus {
box-shadow: 2px 2px 6px #d1d9e6, -2px -2px 6px #f9f9f9;
transform: scale(0.97);
transition: .25s;
}
/**/
.is-txr {
left: calc(100% - 400px);
transition: 1.25s;
transform-origin: left;
}
.is-txl {
left: 0;
transition: 1.25s;
transform-origin: right;
}
.is-z200 {
z-index: 200;
transition: 1.25s;
}
.is-hidden {
visibility: hidden;
opacity: 0;
position: absolute;
transition: 1.25s;
}
.is-gx {
animation: is-gx 1.25s;
}
@keyframes is-gx {
0%,
10%,
100% {
width: 400px;
}
30%,
50% {
width: 500px;
}
}
.form__input :deep(.el-input__inner) {
width: 350px;
height: 40px;
margin: 4px 0;
padding-left: 25px;
font-size: 13px;
letter-spacing: 0.15px;
border: none;
outline: none;
font-family: "Montserrat", sans-serif;
background-color: #ecf0f3;
transition: 0.25s ease;
border-radius: 8px;
box-shadow: inset 3px 3px 3px #d1d9e6, inset -3px -3px 3px #f9f9f9;
cursor: default !important;
}
.form__input :deep(.el-input__wrapper) {
display: inline-flex;
/* flex-grow: 1; */
/* align-items: center; */
/* justify-content: center; */
/* padding: 1px 11px; */
background-color: transparent;
background-image: none;
cursor: default;
/*border-radius: var(--el-input-border-radius,var(--el-border-radius-base));*/
transition: var(--el-transition-box-shadow);
border: none;
box-shadow: 0 0 0 1px var(--el-input-border-color, var(--el-border-color)) inset;
}
.el-alert {
margin: 20px 0 0;
}
.el-alert:first-child {
margin: 0;
}
</style>

@ -0,0 +1,31 @@
<template>
<div class="not-found">
<h1>404 - 页面未找到</h1>
<p>您访问的页面不存在</p>
<el-button @click="goBack"></el-button>
</div>
</template>
<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()
const goBack = () => {
router.go(-1)
}
</script>
<style scoped>
.not-found {
text-align: center;
margin-top: 50px;
}
h1 {
color: #ff4d4f;
}
p {
font-size: 18px;
color: #666;
}
</style>

@ -0,0 +1,125 @@
<template>
<div class="forgot-password">
<el-card class="card">
<template #header>
<h2>忘记密码</h2>
</template>
<el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
<!-- 手机号输入 -->
<el-form-item label="手机号" prop="mobile">
<el-input v-model="form.mobile" placeholder="请输入手机号" />
</el-form-item>
<!-- 验证码输入 -->
<el-form-item label="验证码" prop="captcha">
<el-row>
<el-col :span="16">
<el-input v-model="form.captcha" placeholder="请输入验证码" />
</el-col>
<el-col :span="8">
<el-button :loading="loading" @click="getCaptcha" class="captcha-btn">获取验证码</el-button>
</el-col>
</el-row>
</el-form-item>
<!-- 新密码输入 -->
<el-form-item label="新密码" prop="newPassword">
<el-input type="password" v-model="form.newPassword" placeholder="请输入新密码" />
</el-form-item>
<!-- 确认密码输入 -->
<el-form-item label="确认密码" prop="confirmPassword">
<el-input type="password" v-model="form.confirmPassword" placeholder="请确认密码" />
</el-form-item>
<!-- 提交按钮 -->
<el-form-item>
<el-button type="primary" @click="resetPassword"></el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue';
import { ElMessage } from 'element-plus';
const form = reactive({
mobile: '',
captcha: '',
newPassword: '',
confirmPassword: '',
});
const loading = ref(false);
const rules = reactive({
mobile: [
{ required: true, message: '请输入手机号', trigger: 'blur' },
{ pattern: /^[1][3-9][0-9]{9}$/, message: '请输入有效的手机号', trigger: 'blur' }
],
captcha: [
{ required: true, message: '请输入验证码', trigger: 'blur' },
],
newPassword: [
{ required: true, message: '请输入新密码', trigger: 'blur' },
{ min: 6, max: 16, message: '密码长度应在6-16字符之间', trigger: 'blur' }
],
confirmPassword: [
{ required: true, message: '请确认密码', trigger: 'blur' },
{ validator: (rule, value, callback) => {
if (value !== form.newPassword) {
callback(new Error('两次输入的密码不一致'));
} else {
callback();
}
}, trigger: 'blur' }
]
});
//
const getCaptcha = async () => {
if (!form.mobile) {
ElMessage.error('请输入手机号');
return;
}
loading.value = true;
setTimeout(() => {
loading.value = false;
ElMessage.success('验证码已发送');
}, 2000); //
};
//
const resetPassword = () => {
//
if (!form.mobile || !form.captcha || !form.newPassword || !form.confirmPassword) {
ElMessage.error('请填写完整的表单信息');
return;
}
//
ElMessage.success('密码重置成功!');
// API
};
</script>
<style scoped>
.forgot-password {
max-width: 500px;
margin: 0 auto;
padding: 20px;
}
.card {
padding: 20px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
.captcha-btn {
width: 100%;
background-color: #409EFF;
border-color: #409EFF;
}
</style>

@ -0,0 +1,108 @@
<template>
<div class="app-container">
<div class="header">
<h1 class="title">快递后台管理中心</h1>
</div>
<div class="menu">
<el-menu :default-active="activeIndex" :router="true" class="el-menu-demo" mode="horizontal" :ellipsis="false"
@select="handleSelect">
<el-menu-item index="1" :route="{ name: 'personal' }">用户管理</el-menu-item>
<el-menu-item index="2" :route="{ name: 'problem' }">问题中心</el-menu-item>
<el-menu-item index="3" :route="{ name: 'addkuaidi' }">快件录入</el-menu-item>
<el-menu-item index="4" :route="{ name: 'shopp' }">商品/货物管理</el-menu-item>
<el-menu-item index="5" :route="{ name: 'notice' }">公告发布</el-menu-item>
<el-menu-item index="6" :route="{ name: 'login' }" class="menu-item-right">退出</el-menu-item>
</el-menu>
</div>
<div class="content">
<router-view></router-view>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, watchEffect } from 'vue';
import { useRoute } from 'vue-router';
//
const activeIndex = ref('1');
//
const route = useRoute();
//
watchEffect(() => {
switch (route.name) {
case 'personal':
activeIndex.value = '1';
break;
case 'problem':
activeIndex.value = '2';
break;
case 'addkuaidi':
activeIndex.value = '3';
break;
case 'shopp':
activeIndex.value = '4';
break;
case 'notice':
activeIndex.value = '5';
break;
case 'login':
activeIndex.value = '6';
break;
default:
activeIndex.value = '1';
break;
}
});
</script>
<style scoped>
.app-container {
display: relative;
flex-direction: column;
height: 100vh;
}
.header {
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index: 1000;
background-color: #a4bacb;
color: aliceblue;
padding: 10px 20px;
}
.menu {
margin-top: 50px;
}
.title {
font-size: 15px;
text-align: center;
}
.content {
margin-top: 100px;
/* 根据 header 和 menu 的高度调整 */
flex: 1;
overflow-y: auto;
}
@media (max-width: 600px) {
.header h1 {
font-size: 1.5rem;
}
.el-menu--horizontal {
font-size: 0.9rem;
}
}
.menu-item-right {
margin-left: auto;
}
</style>

@ -0,0 +1,9 @@
// src/hooks/useEmitter.js
import { getCurrentInstance } from 'vue'
export default function useEmitter() {
const internalInstance = getCurrentInstance()
const emitter = internalInstance.appContext.config.globalProperties.emitter
return emitter
}

@ -0,0 +1,30 @@
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import * as Icons from '@element-plus/icons-vue'
import mitt from 'mitt';
import Header from './components/Header.vue';
import Footer from './components/Footer.vue';
import backIndex from './components/backIndex.vue';
import router from './router'
import App from './App.vue'
const app = createApp(App)
const emitter = mitt()
Object.keys(Icons).forEach((key) => {
app.component(key, Icons[key]);
});
app.use(ElementPlus)
app.use(router)
app.config.globalProperties.emitter = emitter;
app.component('Header', Header);
app.component('Footer', Footer);
app.component('backIndex', backIndex);
app.mount('#app')

@ -0,0 +1,8 @@
import axios from "axios";
import { ElEmpty } from "element-plus";
const request = axios.create({
baseURL: 'http://localhost:8081'
})
export default request

@ -0,0 +1,130 @@
import { createRouter, createWebHashHistory } from "vue-router"
const routes= [
// 登录相关
{
path: "/",
name: 'login',
component: () => import("../components/Login.vue")
},
{
path: "/backLogin",
name: 'backLogin',
component: () => import("../views/back/backLogin.vue")
},
{
path: '/rescue',
name: 'rescue',
component: () => import("../components/Rescue.vue")
},
{
path: '/backIndex',
name: 'backIndex',
component: () => import('../components/backIndex.vue')
},
// 页面组件
{
path: "/header",
name: 'header',
component: () => import("../components/Header.vue")
},
{
path: '/carousel',
name: 'carousel',
component: () => import("../components/Carousel.vue")
},
{
path: "/footer",
name: 'footer',
component: () => import("../components/Footer.vue")
},
// 前台用户相关页面
{
path: '/home',
name: 'home',
component: () => import("../views/home.vue")
},
{
path: '/delivery',
name: 'delivery',
component: () => import('../views/fore/delivery.vue')
},
{
path: '/check',
name: 'check',
component: () => import('../views/fore/check.vue')
},
{
path: '/service',
name: 'service',
component: () => import('../views/fore/service.vue')
},
{
path: '/address',
name: 'address',
component: () => import('../views/fore/address.vue')
},
{
path: '/profile',
name: 'profile',
component: () => import('../views/fore/profile.vue')
},
{
path: '/complaint',
name: 'complaint',
component: () => import('../views/fore/complaint.vue')
},
{
path: '/feedback',
name: 'feedback',
component: () => import('../views/fore/feedback.vue')
},
// 后台管理页面
{
path: '/shopp',
name: 'shopp',
component: () => import('../views/back/shopp.vue')
},
{
path: '/addkuaidi',
name: 'addkuaidi',
component: () => import('../views/back/addkuaidi.vue')
},
{
path: '/personal',
name: 'personal',
component: () => import('../views/back/personal.vue')
},
{
path: '/problem',
name: 'problem',
component: () => import('../views/back/problem.vue')
},
{
path: '/notice',
name: 'notice',
component: () => import('../views/back/notice.vue')
},
// 快递员页面
{
path: '/indexDeliveryman',
name: 'indexDeliveryman',
component: () => import('../views/courier/index.vue')
},
// 404 页面
{
path: '/:pathMatch(.*)*',
component: () => import('../components/NotFound.vue')
}
]
const router = createRouter({
history: createWebHashHistory(), routes
})
export default router

@ -0,0 +1,18 @@
import {
defineStore
}
from "pinia";
export const userStore = defineStore('storeId', {
state: () => {
return {
loginState: false // 登录状态, 已经登录true没有登录false
}
},
getters: {},
actions: {
setLoginState(state) {
this.loginState = state
}
}
})

@ -0,0 +1,79 @@
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
}
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
.card {
padding: 2em;
}
#app {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}

@ -0,0 +1,34 @@
<template>
<el-container>
<el-header>
<Header />
</el-header>
<el-main>
<Carousel />
</el-main>
<el-footer>
<Footer />
</el-footer>
</el-container>
</template>
<script setup>
import Header from '../components/Header.vue';
import Carousel from '../components/Carousel.vue';
import Footer from '../components/Footer.vue';
</script>
<style>
.el-aside {
margin-top: 100px;
}
.el-main {
margin-top: 80px;
height: 400px;
}
.el-footer {
margin-top: 100px;
}
</style>

@ -0,0 +1,135 @@
<template>
<el-container>
<!-- 顶部导航 -->
<el-header>
<backIndex />
</el-header>
<!-- 主内容区域 -->
<el-container>
<el-main>
<div style="
padding: 20px;
max-width: 600px;
margin: 30px auto;
background: #f9f9f9;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
">
<h2>录入快件</h2>
<!-- 表单部分 -->
<el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
<el-form-item label="单号" prop="trackingNumber">
<el-input v-model="form.trackingNumber" placeholder="请输入快递单号" />
</el-form-item>
<el-form-item label="快递公司" prop="company">
<el-select v-model="form.company" placeholder="请选择快递公司">
<el-option label="顺丰速运" value="顺丰速运" />
<el-option label="中通快递" value="中通快递" />
<el-option label="圆通速递" value="圆通速递" />
<el-option label="韵达快递" value="韵达快递" />
<el-option label="京东物流" value="京东物流" />
<el-option label="菜鸟裹裹" value="菜鸟裹裹" />
</el-select>
</el-form-item>
<el-form-item label="收件人姓名" prop="recipientName">
<el-input v-model="form.recipientName" placeholder="请输入收件人姓名" />
</el-form-item>
<el-form-item label="手机号" prop="phoneNumber">
<el-input v-model="form.phoneNumber" placeholder="请输入手机号" />
</el-form-item>
<el-form-item label="管理员姓名" prop="adminName">
<el-input v-model="form.adminName" placeholder="请输入管理员姓名" />
</el-form-item>
</el-form>
<!-- 提交按钮 -->
<div style="text-align: center; margin-top: 20px;">
<el-button type="primary" :disabled="!form.trackingNumber ||
!form.company ||
!form.recipientName ||
!form.phoneNumber ||
!form.adminName
" @click="submitForm">
立即提交
</el-button>
</div>
</div>
</el-main>
</el-container>
<!-- 底部信息 -->
<el-footer>
<Footer />
</el-footer>
</el-container>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue';
//
const form = reactive({
trackingNumber: '',
company: '',
recipientName: '',
phoneNumber: '',
adminName: '',
});
//
const rules = {
trackingNumber: [{ required: true, message: '请输入快递单号', trigger: 'blur' }],
company: [{ required: true, message: '请选择快递公司', trigger: 'change' }],
recipientName: [{ required: true, message: '请输入收件人姓名', trigger: 'blur' }],
phoneNumber: [{ required: true, message: '请输入手机号', trigger: 'blur' }],
adminName: [{ required: true, message: '请输入管理员姓名', trigger: 'blur' }],
};
//
const formRef = ref(null);
//
const submitForm = () => {
if (formRef) {
console.log('表单提交成功', form);
alert('快递信息提交成功!');
} else {
alert('请填写完整信息!');
}
// });
};
</script>
<style scoped>
h2 {
text-align: center;
font-size: 24px;
font-weight: bold;
margin-bottom: 30px;
color: #333;
}
.el-form-item {
margin-bottom: 20px;
border-bottom: 1px solid #eee;
padding-bottom: 10px;
}
div[style*='max-width: 600px;'] {
background-color: #f9f9f9;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
@media (max-width: 768px) {
div[style*='max-width: 600px;'] {
max-width: 90%;
padding: 15px;
}
h2 {
font-size: 20px;
}
}
</style>

@ -0,0 +1,233 @@
<template>
<div class="login-container">
<el-card class="login-card">
<template #header>
<h2 class="card-title">后台登陆</h2>
</template>
<el-form :model="foreuser" :rules="rules" ref="ruleFormRef" action="#">
<el-row :gutter="10" class="form-row">
<el-col :span="4" :offset="1">
<span class="label">账号<span v-if="!foreuser.username" class="required">*</span></span>
</el-col>
<el-col :span="15" :offset="0">
<el-form-item prop="username">
<el-input placeholder="账号" class="input" v-model="foreuser.username" :prefix-icon="User" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="10" class="form-row">
<el-col :span="4" :offset="1">
<span class="label">密码<span v-if="!foreuser.password" class="required">*</span></span>
</el-col>
<el-col :span="15" :offset="0">
<el-form-item prop="password">
<el-input placeholder="密码" class="input" v-model="foreuser.password" :prefix-icon="Lock" show-password />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="10" class="form-row">
<el-col :span="5" :offset="0">
<span class="label">用户类型</span>
</el-col>
<el-col :span="15" :offset="0">
<el-form-item>
<el-select v-model="userType" placeholder="点击选择" class="select">
<el-option label="管理员" value="0" />
<el-option label="快递员" value="1" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-form-item>
<a href="#" class="link">忘记密码</a>
</el-form-item>
<el-form-item>
<el-button class="btn primary-btn" type="primary" @click="login"></el-button>
<el-button class="btn secondary-btn" @click="goBack"></el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue';
import { User, Lock } from '@element-plus/icons-vue';
import { ElMessage } from 'element-plus';
import { useRouter } from 'vue-router';
const foreuser = reactive({ username: '', password: '', mobile: '' });
const router = useRouter();
const userType = ref('');
const rules = reactive({
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
],
});
//
const adims = [
{ mobile: 'admin', password: '123456' },
];
//
const users = [
{ mobile: 'user', password: '123456' },
];
//
const login = () => {
if (!userType.value) {
ElMessage.error('请选择用户类型');
return;
}
//
if (!foreuser.username || !foreuser.password) {
ElMessage.error('请输入完整的账号和密码');
return;
}
//
if (userType.value === '0') { //
const admin = adims.find(item => item.mobile === foreuser.username && item.password === foreuser.password);
if (admin) {
ElMessage.success('登陆成功!');
router.push({ name: 'notice' }); //
} else {
ElMessage.error('用户名或密码错误!');
}
} else if (userType.value === '1') { //
const user = users.find(item => item.mobile === foreuser.username && item.password === foreuser.password);
if (user) {
ElMessage.success('登陆成功!');
router.push({ name: 'indexDeliveryman' }); //
} else {
ElMessage.error('用户名或密码错误!');
}
}
};
const goBack = () => {
router.go(-1)
}
</script>
<style scoped>
.login-container {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
}
.login-card {
background-color: #fff;
padding: 40px;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
width: 400px;
max-width: 100%;
}
.card-title {
font-size: 24px;
font-weight: bold;
color: #333;
text-align: center;
margin-bottom: 20px;
}
.form-row {
margin-bottom: 10px; /* 增加每个输入框之间的间距 */
}
.input {
margin-bottom: 20px;
border: 1px solid #ccc;
border-radius: 5px;
background-color: #fff;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.input input {
border: none;
outline: none;
box-shadow: none;
}
.select {
width: 100%;
margin-bottom: 20px;
border: 1px solid #ccc;
border-radius: 5px;
background-color: #fff;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.select select {
border: none;
outline: none;
box-shadow: none;
}
.link {
color: #409eff;
text-decoration: none;
cursor: pointer;
float: right;
margin-left: 220px;
}
.link:hover {
text-decoration: underline;
}
.btn {
margin-right: 10px;
padding: 10px 20px;
font-size: 16px;
border-radius: 5px;
transition: all 0.3s ease;
}
.primary-btn {
background-color: #409eff;
color: #fff;
margin-left: 100px;
}
.primary-btn:hover {
background-color: #66b1ff;
}
.secondary-btn {
background-color: #fff;
color: #409eff;
border: 1px solid #409eff;
}
.secondary-btn:hover {
background-color: #e4e7ed;
color: #303133;
}
.label {
display: block;
margin-bottom: 5px;
font-size: 14px;
color: #606266;
}
.required {
color: red;
margin-left: 2px;
}
</style>

@ -0,0 +1,527 @@
<template>
<el-container>
<el-header>
<backIndex />
</el-header>
<el-container>
<el-main class="emain">
<div class="management-container">
<!-- 用户管理 -->
<div class="section">
<h3>用户管理</h3>
</div>
<div>
<!-- 搜索框和添加用户按钮的容器 -->
<div class="toolbar">
<!-- 用户搜索框 -->
<el-input v-model="userSearch" placeholder="输入员工id或用户id" class="user-search" prefix-icon="el-icon-search">
</el-input>
<!-- 添加用户按钮 -->
<el-button @click="handleAddUser" type="primary" class="add-user-button">
添加用户
</el-button>
</div>
<!-- 用户表格 -->
<el-table :data="paginatedUsers" stripe style="width: 90%; margin: 20px auto;" border>
<el-table-column prop="id" label="用户ID" align="center"></el-table-column>
<el-table-column prop="email" label="邮箱" align="center"></el-table-column>
<el-table-column prop="password" label="密码" align="center"></el-table-column>
<el-table-column prop="phone" label="电话号" align="center"></el-table-column>
<el-table-column prop="lastLogin" label="最后登录时间" align="center"></el-table-column>
<el-table-column prop="registerTime" label="注册时间" align="center"></el-table-column>
<el-table-column label="操作" width="220" align="center">
<template v-slot="scope">
<div class="button-group">
<el-button @click="handleEditUser(scope.row)" size="mini" type="primary" plain>编辑</el-button>
<el-button @click="handleDeleteUser(scope.row)" size="mini" type="danger" plain>删除</el-button>
<el-button @click="handleViewUser(scope.row)" size="mini" type="info" plain>查看详情</el-button>
</div>
</template>
</el-table-column>
</el-table>
<!-- 分页控件 -->
<el-pagination :current-page="userCurrentPage" :page-size="userPageSize" :total="userData.length"
@current-change="handleUserPageChange" layout="prev, pager, next, jumper" class="pagination" />
</div>
<!-- 快递员管理 -->
<div class="section">
<h3>快递员管理</h3>
<!-- 添加快递员按钮 -->
<div class="action-buttons">
<el-button @click="handleAddCourier" type="primary">添加快递员</el-button>
</div>
<el-table :data="paginatedCouriers" stripe style="width: 90%; margin: 0 auto;" border>
<el-table-column prop="id" label="员工号" align="center"></el-table-column>
<el-table-column prop="name" label="姓名" align="center"></el-table-column>
<el-table-column prop="status" label="在职状态" align="center"></el-table-column>
<el-table-column prop="phone" label="电话" align="center"></el-table-column>
<el-table-column prop="entryTime" label="入职时间" align="center"></el-table-column>
<el-table-column label="操作" width="220" align="center">
<template v-slot="scope">
<div class="button-group">
<el-button @click="handleEditCourier(scope.row)" size="mini" type="primary" plain>编辑</el-button>
<el-button @click="handleDeleteCourier(scope.row)" size="mini" type="danger" plain>删除</el-button>
<el-button @click="handleViewCourier(scope.row)" size="mini" type="info" plain>查看详情</el-button>
</div>
</template>
</el-table-column>
</el-table>
<!-- 分页控件 -->
<el-pagination :current-page="courierCurrentPage" :page-size="courierPageSize" :total="courierData.length"
@current-change="handleCourierPageChange" layout="prev, pager, next, jumper" class="pagination" />
</div>
</div>
</el-main>
</el-container>
<el-footer>
<Footer />
</el-footer>
<!-- 编辑用户对话框 -->
<el-dialog title="编辑用户" v-model="editUserDialogVisible">
<el-form :model="currentUser">
<el-form-item label="用户ID">
<el-input v-model="currentUser.id" readonly />
</el-form-item>
<el-form-item label="邮箱">
<el-input v-model="currentUser.email" />
</el-form-item>
<el-form-item label="密码">
<el-input v-model="currentUser.password" />
</el-form-item>
<el-form-item label="电话号">
<el-input v-model="currentUser.phone" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="editUserDialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSaveUserEdit"></el-button>
</div>
</el-dialog>
<!-- 添加用户对话框 -->
<el-dialog title="添加用户" v-model="addUserDialogVisible">
<el-form :model="newUser" label-width="100px">
<el-form-item label="用户ID">
<el-input v-model="newUser.id" placeholder="请输入用户ID" />
</el-form-item>
<el-form-item label="邮箱">
<el-input v-model="newUser.email" placeholder="请输入邮箱" />
</el-form-item>
<el-form-item label="密码">
<el-input v-model="newUser.password" placeholder="请输入密码" />
</el-form-item>
<el-form-item label="电话号">
<el-input v-model="newUser.phone" placeholder="请输入电话号码" />
</el-form-item>
<el-form-item label="注册时间">
<el-date-picker v-model="newUser.registerTime" type="date" placeholder="选择注册时间" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="addUserDialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSaveNewUser"></el-button>
</div>
</el-dialog>
<!-- 编辑快递员对话框 -->
<el-dialog title="编辑快递员" v-model="editCourierDialogVisible">
<el-form :model="currentCourier">
<el-form-item label="员工号">
<el-input v-model="currentCourier.id" readonly />
</el-form-item>
<el-form-item label="姓名">
<el-input v-model="currentCourier.name" />
</el-form-item>
<el-form-item label="在职状态">
<el-input v-model="currentCourier.status" />
</el-form-item>
<el-form-item label="电话">
<el-input v-model="currentCourier.phone" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="editCourierDialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSaveCourierEdit"></el-button>
</div>
</el-dialog>
<!-- 添加快递员对话框 -->
<el-dialog title="添加快递员" v-model="addCourierDialogVisible">
<el-form :model="newCourier" label-width="100px">
<el-form-item label="员工号">
<el-input v-model="newCourier.id" placeholder="请输入员工号" />
</el-form-item>
<el-form-item label="姓名">
<el-input v-model="newCourier.name" placeholder="请输入姓名" />
</el-form-item>
<el-form-item label="在职状态">
<el-input v-model="newCourier.status" placeholder="请输入在职状态(如:在职、离职)" />
</el-form-item>
<el-form-item label="电话">
<el-input v-model="newCourier.phone" placeholder="请输入电话号码" />
</el-form-item>
<el-form-item label="入职时间">
<el-date-picker v-model="newCourier.entryTime" type="date" placeholder="选择入职时间" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="addCourierDialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSaveNewCourier"></el-button>
</div>
</el-dialog>
<!-- 查看快递员详情对话框 -->
<el-dialog title="快递员详情" v-model="viewCourierDialogVisible">
<el-form :model="currentCourier" label-width="100px">
<el-form-item label="员工号">
<el-input v-model="currentCourier.id" readonly />
</el-form-item>
<el-form-item label="姓名">
<el-input v-model="currentCourier.name" readonly />
</el-form-item>
<el-form-item label="在职状态">
<el-input v-model="currentCourier.status" readonly />
</el-form-item>
<el-form-item label="电话">
<el-input v-model="currentCourier.phone" readonly />
</el-form-item>
<el-form-item label="入职时间">
<el-input v-model="currentCourier.entryTime" readonly />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="viewCourierDialogVisible = false">关闭</el-button>
</div>
</el-dialog>
<!-- 查看详情对话框 -->
<!-- 查看用户详情对话框 -->
<el-dialog title="查看用户详情" v-model="viewUserDialogVisible">
<el-form :model="currentUser" label-width="100px" readonly>
<el-form-item label="用户ID">
<el-input v-model="currentUser.id" readonly />
</el-form-item>
<el-form-item label="邮箱">
<el-input v-model="currentUser.email" readonly />
</el-form-item>
<el-form-item label="密码">
<el-input v-model="currentUser.password" readonly />
</el-form-item>
<el-form-item label="电话号">
<el-input v-model="currentUser.phone" readonly />
</el-form-item>
<el-form-item label="最后登录时间">
<el-input v-model="currentUser.lastLogin" readonly />
</el-form-item>
<el-form-item label="注册时间">
<el-input v-model="currentUser.registerTime" readonly />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="viewUserDialogVisible = false">关闭</el-button>
</div>
</el-dialog>
</el-container>
</template>
<script lang="ts" setup>
import { ref, computed } from 'vue'
import { ElMessageBox, ElMessage } from 'element-plus'
//
const viewUserDialogVisible = ref(false)
const handleViewUser = (user) => {
currentUser.value = { ...user } //
viewUserDialogVisible.value = true //
}
//
const viewCourierDialogVisible = ref(false) //
const handleViewCourier = (courier: any) => {
currentCourier.value = { ...courier } //
viewCourierDialogVisible.value = true //
}
//
const addUserDialogVisible = ref(false) //
const newUser = ref({
id: '', // ID
email: '', //
password: '', //
phone: '', //
registerTime: '' //
})
const handleAddUser = () => {
newUser.value = { id: '', email: '', password: '', phone: '', registerTime: '' } //
addUserDialogVisible.value = true //
}
const handleSaveNewUser = () => {
if (!newUser.value.id || !newUser.value.email || !newUser.value.phone) {
ElMessage.warning('请完整填写信息')
return
}
//
ElMessage.success('用户添加成功')
addUserDialogVisible.value = false //
}
//
const userData = ref([
{ id: '001', email: 'user1@example.com', password: 'password123', phone: '13800000001', lastLogin: '2024-11-01 10:30:00', registerTime: '2023-01-15 14:20:00' },
{ id: '002', email: 'user2@example.com', password: 'password123', phone: '13800000002', lastLogin: '2024-11-02 12:00:00', registerTime: '2023-02-10 09:15:00' },
// ...
]);
const courierData = ref([
{ id: '001', name: '小刘', status: '在职', phone: '13800000001', entryTime: '2022-03-01' },
{ id: '002', name: '小王', status: '离职', phone: '13800000002', entryTime: '2021-06-15' },
// ...
{ id: '001', name: '小刘', status: '在职', phone: '13800000001', entryTime: '2022-03-01' },
{ id: '002', name: '小王', status: '离职', phone: '13800000002', entryTime: '2021-06-15' },
{ id: '001', name: '小刘', status: '在职', phone: '13800000001', entryTime: '2022-03-01' },
{ id: '002', name: '小王', status: '离职', phone: '13800000002', entryTime: '2021-06-15' },
{ id: '001', name: '小刘', status: '在职', phone: '13800000001', entryTime: '2022-03-01' },
{ id: '002', name: '小王', status: '离职', phone: '13800000002', entryTime: '2021-06-15' },
{ id: '001', name: '小刘', status: '在职', phone: '13800000001', entryTime: '2022-03-01' },
{ id: '002', name: '小王', status: '离职', phone: '13800000002', entryTime: '2021-06-15' },
{ id: '001', name: '小刘', status: '在职', phone: '13800000001', entryTime: '2022-03-01' },
{ id: '002', name: '小王', status: '离职', phone: '13800000002', entryTime: '2021-06-15' },
{ id: '001', name: '小刘', status: '在职', phone: '13800000001', entryTime: '2022-03-01' },
{ id: '002', name: '小王', status: '离职', phone: '13800000002', entryTime: '2021-06-15' },
{ id: '001', name: '小刘', status: '在职', phone: '13800000001', entryTime: '2022-03-01' },
{ id: '002', name: '小王', status: '离职', phone: '13800000002', entryTime: '2021-06-15' },
{ id: '001', name: '小刘', status: '在职', phone: '13800000001', entryTime: '2022-03-01' },
{ id: '002', name: '小王', status: '离职', phone: '13800000002', entryTime: '2021-06-15' },
{ id: '001', name: '小刘', status: '在职', phone: '13800000001', entryTime: '2022-03-01' },
{ id: '002', name: '小王', status: '离职', phone: '13800000002', entryTime: '2021-06-15' },
]);
// /
const currentUser = ref<any>({})
const currentCourier = ref<any>({})
//
const editUserDialogVisible = ref(false)
const editCourierDialogVisible = ref(false)
//
const userPageSize = 5
const courierPageSize = 5
const userCurrentPage = ref(1)
const courierCurrentPage = ref(1)
const userSearch = ref('')
//
const addCourierDialogVisible = ref(false) //
const newCourier = ref({
id: '', //
name: '', //
status: '', //
phone: '', //
entryTime: '' //
})
const handleAddCourier = () => {
newCourier.value = { id: '', name: '', status: '', phone: '', entryTime: '' } //
addCourierDialogVisible.value = true //
}
const handleSaveNewCourier = () => {
if (!newCourier.value.id || !newCourier.value.name || !newCourier.value.phone) {
ElMessage.warning('请完整填写信息')
return
}
//
courierData.value.push({ ...newCourier.value })
ElMessage.success('快递员添加成功')
addCourierDialogVisible.value = false //
}
//
const paginatedUsers = computed(() => {
const start = (userCurrentPage.value - 1) * userPageSize
return userData.value.slice(start, start + userPageSize)
})
const paginatedCouriers = computed(() => {
const start = (courierCurrentPage.value - 1) * courierPageSize
return courierData.value.slice(start, start + courierPageSize)
})
//
const handleEditUser = (user: any) => {
currentUser.value = { ...user }
editUserDialogVisible.value = true
}
//
const handleEditCourier = (courier: any) => {
currentCourier.value = { ...courier }
editCourierDialogVisible.value = true
}
//
const handleDeleteUser = (user: any) => {
ElMessageBox.confirm(
'确定删除该用户吗?',
'删除确认',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
//
const index = userData.value.findIndex(u => u.id === user.id)
if (index !== -1) userData.value.splice(index, 1)
ElMessage.success('删除成功')
}).catch(() => {
ElMessage.info('删除已取消')
})
}
//
const handleDeleteCourier = (courier: any) => {
ElMessageBox.confirm(
'确定删除该快递员吗?',
'删除确认',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
//
const index = courierData.value.findIndex(c => c.id === courier.id)
if (index !== -1) courierData.value.splice(index, 1)
ElMessage.success('删除成功')
}).catch(() => {
ElMessage.info('删除已取消')
})
}
//
const handleSaveUserEdit = () => {
const index = userData.value.findIndex(u => u.id === currentUser.value.id)
if (index !== -1) {
userData.value[index] = { ...currentUser.value }
ElMessage.success('用户信息已更新')
}
editUserDialogVisible.value = false
}
//
const handleSaveCourierEdit = () => {
const index = courierData.value.findIndex(c => c.id === currentCourier.value.id)
if (index !== -1) {
courierData.value[index] = { ...currentCourier.value }
ElMessage.success('快递员信息已更新')
}
editCourierDialogVisible.value = false
}
//
const handleUserPageChange = (page: number) => userCurrentPage.value = page
const handleCourierPageChange = (page: number) => courierCurrentPage.value = page
</script>
<style scoped>
.toolbar {
display: flex;
justify-content: flex-end;
/* 将内容对齐到右边 */
align-items: center;
width: 90%;
margin: 20px auto;
}
.user-search {
flex: 0 0 200px;
/* 搜索框的固定宽度 */
margin-right: 10px;
/* 可以根据需要调整这个值 */
}
/* 添加用户按钮 */
.add-user-button {
flex-shrink: 0;
}
.emain {
margin-top: 50px;
}
.management-container {
padding: 20px;
background-color: #f9f9f9;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
text-align: center;
}
.management-container h2 {
font-size: 24px;
color: #333;
margin-bottom: 20px;
}
.section {
margin-top: 30px;
}
.section h3 {
font-size: 20px;
color: #666;
margin-bottom: 15px;
text-align: center;
}
.el-table {
margin-top: 20px;
}
.button-group {
display: flex;
justify-content: space-evenly;
}
.el-button {
min-width: 60px;
font-size: 12px;
padding: 4px 8px;
}
.pagination {
margin-top: 20px;
display: flex;
justify-content: center;
}
.el-table-column {
text-align: center;
}
.el-table__header-wrapper th {
text-align: center;
}
.action-buttons {
text-align: right;
margin-top: 20px;
margin-right: 100px;
margin-bottom: 20px;
}
</style>

@ -0,0 +1,271 @@
<template>
<el-container>
<el-header>
<backIndex />
</el-header>
<el-container>
<el-main class="emain">
<div class="problem-container">
<!-- 用户问题表格 -->
<div class="section">
<h3>用户问题</h3>
<el-table :data="paginatedUserProblems" stripe style="width: 90%; margin: 0 auto;" border>
<el-table-column prop="id" label="ID" align="center"></el-table-column>
<el-table-column prop="username" label="用户名" align="center"></el-table-column>
<el-table-column prop="title" label="问题标题" align="center" header-align="center"></el-table-column>
<el-table-column prop="status" label="问题状态" align="center">
<template v-slot="scope">
<el-tag :type="scope.row.status === '已解决' ? 'success' : 'warning'" disable-transitions>
{{ scope.row.status }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="merchantStatus" label="商家回复" width="120" align="center">
<template v-slot="scope">
<el-tag :type="scope.row.merchantStatus === '已回复' ? 'success' : 'danger'" disable-transitions>
{{ scope.row.merchantStatus }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="220" align="center">
<template v-slot="scope">
<div class="button-group">
<el-button @click="handleResolve(scope.row)" size="mini" type="success" plain>解决</el-button>
<el-button @click="handleDelete(scope.row)" size="mini" type="danger" plain>删除</el-button>
<el-button @click="handleContact(scope.row)" size="mini" type="primary" plain>联系客户</el-button>
</div>
</template>
</el-table-column>
</el-table>
<!-- 分页控件 -->
<el-pagination :current-page="userCurrentPage" :page-size="userPageSize" :total="userProblemData.length"
@current-change="handleUserPageChange" layout="prev, pager, next, jumper" class="pagination" />
</div>
<!-- 快递员问题表格 -->
<div class="section">
<h3>快递员问题</h3>
<el-table :data="paginatedCourierProblems" stripe style="width: 90%; margin: 0 auto;" border>
<el-table-column prop="id" label="ID" align="center"></el-table-column>
<el-table-column prop="courierName" label="快递员姓名" align="center"></el-table-column>
<el-table-column prop="title" label="问题标题" align="center" header-align="center"></el-table-column>
<el-table-column prop="status" label="问题状态" align="center">
<template v-slot="scope">
<el-tag :type="scope.row.status === '已解决' ? 'success' : 'warning'" disable-transitions>
{{ scope.row.status }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="companyReply" label="公司回复" width="120" align="center">
<template v-slot="scope">
<el-tag :type="scope.row.companyReply === '已回复' ? 'success' : 'danger'" disable-transitions>
{{ scope.row.companyReply }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="220" align="center">
<template v-slot="scope">
<div class="button-group">
<el-button @click="handleResolve(scope.row)" size="mini" type="success" plain>解决</el-button>
<el-button @click="handleDelete(scope.row)" size="mini" type="danger" plain>删除</el-button>
<el-button @click="handleContactCourier(scope.row)" size="mini" type="primary"
plain>联系快递员</el-button>
</div>
</template>
</el-table-column>
</el-table>
<!-- 分页控件 -->
<el-pagination :current-page="courierCurrentPage" :page-size="courierPageSize"
:total="courierProblemData.length" @current-change="handleCourierPageChange"
layout="prev, pager, next, jumper" class="pagination" />
</div>
</div>
</el-main>
</el-container>
<el-footer>
<Footer />
</el-footer>
</el-container>
</template>
<script lang="ts" setup>
import { ref, computed } from 'vue'
const userProblemData = ref([
{ id: 1, username: '张三', title: '快递丢失', status: '未解决', merchantStatus: '未回复' },
{ id: 2, username: '李四', title: '配送延迟', status: '已解决', merchantStatus: '已回复' },
{ id: 3, username: '王五', title: '包裹损坏', status: '未解决', merchantStatus: '未回复' },
{ id: 4, username: '赵六', title: '地址错误', status: '未解决', merchantStatus: '未回复' },
{ id: 5, username: '孙七', title: '包装破损', status: '已解决', merchantStatus: '已回复' },
{ id: 6, username: '周八', title: '收件人地址错误', status: '未解决', merchantStatus: '未回复' },
{ id: 7, username: '吴九', title: '物品破损', status: '未解决', merchantStatus: '未回复' },
{ id: 8, username: '郑十', title: '配送信息不符', status: '未解决', merchantStatus: '未回复' },
{ id: 9, username: '钱十一', title: '签收问题', status: '已解决', merchantStatus: '已回复' },
{ id: 10, username: '孙十二', title: '快递丢失', status: '未解决', merchantStatus: '未回复' },
{ id: 11, username: '李十三', title: '包裹迟迟未到', status: '未解决', merchantStatus: '未回复' },
{ id: 12, username: '王十四', title: '派送时效不符合', status: '已解决', merchantStatus: '已回复' },
{ id: 13, username: '赵十五', title: '包装破损', status: '未解决', merchantStatus: '未回复' },
{ id: 14, username: '孙十六', title: '快递迟到', status: '已解决', merchantStatus: '已回复' },
{ id: 15, username: '周十七', title: '配送路线问题', status: '未解决', merchantStatus: '未回复' },
{ id: 16, username: '吴十八', title: '物品丢失', status: '未解决', merchantStatus: '未回复' },
{ id: 17, username: '郑十九', title: '快递员态度差', status: '已解决', merchantStatus: '已回复' },
{ id: 18, username: '钱二十', title: '快递员迟到', status: '未解决', merchantStatus: '未回复' },
{ id: 19, username: '孙二十一', title: '商品损坏', status: '已解决', merchantStatus: '已回复' },
{ id: 20, username: '李二十二', title: '配送不及时', status: '未解决', merchantStatus: '未回复' },
{ id: 21, username: '王二十三', title: '包裹未送达', status: '未解决', merchantStatus: '未回复' },
{ id: 22, username: '赵二十四', title: '地址不清晰', status: '已解决', merchantStatus: '已回复' },
{ id: 23, username: '孙二十五', title: '收件地址错误', status: '未解决', merchantStatus: '未回复' },
{ id: 24, username: '周二十六', title: '派送延误', status: '已解决', merchantStatus: '已回复' },
{ id: 25, username: '吴二十七', title: '包裹破损', status: '未解决', merchantStatus: '未回复' },
{ id: 26, username: '郑二十八', title: '配送信息错误', status: '已解决', merchantStatus: '已回复' },
{ id: 27, username: '钱二十九', title: '包裹无法签收', status: '未解决', merchantStatus: '未回复' },
{ id: 28, username: '孙三十', title: '派送时效问题', status: '已解决', merchantStatus: '已回复' },
{ id: 29, username: '李三十一', title: '快递包裹丢失', status: '未解决', merchantStatus: '未回复' },
{ id: 30, username: '王三十二', title: '物流追踪问题', status: '已解决', merchantStatus: '已回复' }
])
const courierProblemData = ref([
{ id: 1, courierName: '小刘', title: '车辆故障', status: '未解决', companyReply: '未回复' },
{ id: 2, courierName: '小王', title: '路线不清', status: '已解决', companyReply: '已回复' },
{ id: 3, courierName: '小李', title: '客户未取件', status: '未解决', companyReply: '未回复' },
{ id: 4, courierName: '小张', title: '包裹丢失', status: '未解决', companyReply: '未回复' },
{ id: 5, courierName: '小赵', title: '快递迟到', status: '已解决', companyReply: '已回复' },
{ id: 3, courierName: '小李', title: '客户未取件', status: '未解决', companyReply: '未回复' },
{ id: 4, courierName: '小张', title: '包裹丢失', status: '未解决', companyReply: '未回复' },
{ id: 5, courierName: '小赵', title: '快递迟到', status: '已解决', companyReply: '已回复' },
{ id: 6, courierName: '小陈', title: '包裹损坏', status: '未解决', companyReply: '未回复' },
{ id: 7, courierName: '小黄', title: '快递错发', status: '已解决', companyReply: '已回复' },
{ id: 8, courierName: '小李', title: '包裹丢失', status: '未解决', companyReply: '未回复' },
{ id: 9, courierName: '小刘', title: '客户未取件', status: '已解决', companyReply: '已回复' },
{ id: 10, courierName: '小王', title: '快递迟到', status: '未解决', companyReply: '未回复' },
{ id: 11, courierName: '小张', title: '车辆故障', status: '已解决', companyReply: '已回复' },
{ id: 12, courierName: '小赵', title: '路线不清', status: '未解决', companyReply: '未回复' },
{ id: 13, courierName: '小陈', title: '包裹丢失', status: '已解决', companyReply: '已回复' },
{ id: 14, courierName: '小黄', title: '客户未取件', status: '未解决', companyReply: '未回复' },
{ id: 15, courierName: '小李', title: '快递错发', status: '已解决', companyReply: '已回复' },
{ id: 16, courierName: '小刘', title: '车辆故障', status: '未解决', companyReply: '未回复' },
{ id: 17, courierName: '小王', title: '快递迟到', status: '已解决', companyReply: '已回复' },
{ id: 18, courierName: '小张', title: '客户未取件', status: '未解决', companyReply: '未回复' },
{ id: 19, courierName: '小赵', title: '包裹丢失', status: '已解决', companyReply: '已回复' },
{ id: 20, courierName: '小陈', title: '快递错发', status: '未解决', companyReply: '未回复' },
{ id: 21, courierName: '小黄', title: '包裹损坏', status: '已解决', companyReply: '已回复' },
{ id: 22, courierName: '小李', title: '路线不清', status: '未解决', companyReply: '未回复' },
{ id: 23, courierName: '小刘', title: '快递迟到', status: '已解决', companyReply: '已回复' },
{ id: 24, courierName: '小王', title: '车辆故障', status: '未解决', companyReply: '未回复' },
{ id: 25, courierName: '小张', title: '快递错发', status: '已解决', companyReply: '已回复' },
//
])
const userPageSize = 10 //
const courierPageSize = 10 //
const userCurrentPage = ref(1) //
const courierCurrentPage = ref(1) //
//
const paginatedUserProblems = computed(() => {
const start = (userCurrentPage.value - 1) * userPageSize
return userProblemData.value.slice(start, start + userPageSize)
})
//
const paginatedCourierProblems = computed(() => {
const start = (courierCurrentPage.value - 1) * courierPageSize
return courierProblemData.value.slice(start, start + courierPageSize)
})
//
const handleUserPageChange = (page: number) => {
userCurrentPage.value = page
}
//
const handleCourierPageChange = (page: number) => {
courierCurrentPage.value = page
}
const handleResolve = (row: any) => {
console.log('解决问题', row)
row.status = '已解决'
}
const handleDelete = (row: any) => {
console.log('删除问题', row)
}
const handleContact = (row: any) => {
console.log('联系客户', row)
}
const handleContactCourier = (row: any) => {
console.log('联系快递员', row)
}
</script>
<style scoped>
.emain {
margin-top: 50px;
}
.problem-container {
padding: 20px;
background-color: #f9f9f9;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
text-align: center;
}
.problem-container h2 {
font-size: 24px;
color: #333;
margin-bottom: 20px;
}
.section {
margin-top: 40px;
}
.section h3 {
font-size: 20px;
color: #666;
margin-bottom: 15px;
text-align: center;
}
.el-table {
margin-top: 20px;
}
.button-group {
display: flex;
justify-content: space-evenly;
}
.el-button {
min-width: 60px;
font-size: 12px;
padding: 4px 8px;
}
.pagination {
margin-top: 20px;
display: flex;
justify-content: center;
/* 居中分页 */
}
.el-table-column {
text-align: center;
}
.el-table__header-wrapper th {
text-align: center;
}
</style>

@ -0,0 +1,199 @@
<template>
<el-container class="container">
<el-header>
<backIndex />
</el-header>
<el-container class="main-container">
<el-main class="content">
<div class="content-header"></div>
<el-row :gutter="70">
<el-col :xs="24" :sm="12">
<el-input v-model="searchQuery" placeholder="搜索商品名称以进行批量操作" suffix-icon="el-icon-search"
style="width: 130%;" />
</el-col>
<el-col :xs="24" :sm="12" class="batch-actions" style="margin-top: 10px;">
<el-button @click="handleBatchDelete" type="danger" size="small">批量删除</el-button>
<el-button @click="handleBatchStatusChange" type="warning" size="small">批量上架/下架</el-button>
</el-col>
</el-row>
<el-table :data="currentPageGoodsData" style="width: 100%; table-layout: auto">
<el-table-column prop="name" label="商品名称" width="150">
<template v-slot="scope">
<el-checkbox v-model="scope.row.selected" />
{{ scope.row.name }}
</template>
</el-table-column>
<el-table-column prop="id" label="商品ID" width="120" />
<el-table-column prop="category" label="所属分类" width="150" />
<el-table-column prop="price" label="商品价格" width="100" />
<el-table-column prop="audience" label="适用人群" width="120" />
<el-table-column prop="status" label="状态" width="120">
<template v-slot="scope">
<el-tag :type="scope.row.status === '已上架' ? 'success' : 'info'">
{{ scope.row.status }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="250">
<template v-slot="scope">
<el-button @click="editItem(scope.row)" type="primary" size="small">编辑</el-button>
<el-button @click="deleteItem(scope.row)" type="danger" size="small">删除</el-button>
<el-button @click="toggleStatus(scope.row)" :type="scope.row.status === '已上架' ? 'warning' : 'success'"
size="small">
{{ scope.row.status === '已上架' ? '下架' : '上架' }}
</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination class="pagination" :current-page="currentPage" :page-size="pageSize"
:total="filteredGoodsData.length" @current-change="handlePageChange"
layout="total, prev, pager, next, jumper" />
</el-main>
</el-container>
<el-footer>
<Footer />
</el-footer>
<!-- 弹窗 -->
<!-- 弹窗 -->
<el-dialog :visible.sync="deleteDialogVisible" title="确认删除">
<p>您确定要删除商品 "{{ itemToDelete ? itemToDelete.name : '' }}" </p>
<span slot="footer" class="dialog-footer">
<el-button @click="cancelDelete"></el-button>
<el-button type="danger" @click="confirmDelete"></el-button>
</span>
</el-dialog>
</el-container>
</template>
<script lang="ts" setup>
import { ref, computed } from 'vue';
import { ElMessage } from 'element-plus';
const searchQuery = ref('');
const currentPage = ref(1);
const pageSize = ref(11);
const itemToDeleteName = computed(() => itemToDelete.value?.name || '');
const deleteDialogVisible = ref(false);
const editForm = ref<any>({});
const itemToDelete = ref<any>(null); //
const deleteItem = (item: any) => {
itemToDelete.value = item; //
deleteDialogVisible.value = true; //
};
const cancelDelete = () => {
itemToDelete.value = null; //
deleteDialogVisible.value = false; //
};
const confirmDelete = () => {
if (itemToDelete.value) {
goodsData.value = goodsData.value.filter(item => item.id !== itemToDelete.value.id);
ElMessage.success(`商品 "${itemToDelete.value.name}" 删除成功`);
itemToDelete.value = null; //
}
deleteDialogVisible.value = false; //
};
const goodsData = ref([
{ name: '商品1', id: '001', category: '电子产品', price: '100', audience: '成年人', status: '已下架', selected: false },
{ name: '商品2', id: '002', category: '家居用品', price: '50', audience: '家庭', status: '已上架', selected: false },
{ name: '商品1', id: '003', category: '电子产品', price: '100', audience: '成年人', status: '已下架', selected: false },
{ name: '商品2', id: '004', category: '家居用品', price: '50', audience: '家庭', status: '已上架', selected: false },
{ name: '商品1', id: '005', category: '电子产品', price: '100', audience: '成年人', status: '已下架', selected: false },
{ name: '商品2', id: '006', category: '家居用品', price: '50', audience: '家庭', status: '已上架', selected: false },
{ name: '商品1', id: '007', category: '电子产品', price: '100', audience: '成年人', status: '已下架', selected: false },
{ name: '商品2', id: '008', category: '家居用品', price: '50', audience: '家庭', status: '已上架', selected: false },
{ name: '商品1', id: '009', category: '电子产品', price: '100', audience: '成年人', status: '已下架', selected: false },
{ name: '商品2', id: '010', category: '家居用品', price: '50', audience: '家庭', status: '已上架', selected: false },
{ name: '商品1', id: '011', category: '电子产品', price: '100', audience: '成年人', status: '已下架', selected: false },
{ name: '商品2', id: '012', category: '家居用品', price: '50', audience: '家庭', status: '已上架', selected: false },
{ name: '商品1', id: '013', category: '电子产品', price: '100', audience: '成年人', status: '已下架', selected: false },
{ name: '商品2', id: '014', category: '家居用品', price: '50', audience: '家庭', status: '已上架', selected: false },
]);
const filteredGoodsData = computed(() => {
if (!searchQuery.value) return goodsData.value;
return goodsData.value.filter(item => item.name.includes(searchQuery.value));
});
const currentPageGoodsData = computed(() => {
const start = (currentPage.value - 1) * pageSize.value;
const end = start + pageSize.value;
return filteredGoodsData.value.slice(start, end);
});
const handlePageChange = (page: number) => {
currentPage.value = page;
};
const handleBatchDelete = () => {
const selectedItems = goodsData.value.filter(item => item.selected);
if (selectedItems.length === 0) {
return ElMessage.warning('请先选择要删除的商品');
}
deleteDialogVisible.value = true;
};
const confirmBatchDelete = () => {
goodsData.value = goodsData.value.filter(item => !item.selected);
deleteDialogVisible.value = false;
ElMessage.success('删除成功');
};
const handleBatchStatusChange = () => {
const selectedItems = goodsData.value.filter(item => item.selected);
if (selectedItems.length === 0) {
return ElMessage.warning('请先选择要更改状态的商品');
}
selectedItems.forEach(item => {
item.status = item.status === '已上架' ? '已下架' : '已上架';
});
ElMessage.success('状态修改成功');
};
const editItem = (item: any) => {
editForm.value = { ...item };
};
const toggleStatus = (item: any) => {
item.status = item.status === '已上架' ? '已下架' : '已上架';
};
</script>
<style scoped>
.main-container {
display: flex;
justify-content: center;
align-items: flex-start;
padding-top: 50px;
height: 100vh;
box-sizing: border-box;
}
.pagination {
margin: 20px auto;
}
.content {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
max-width: 1200px;
margin: 0 auto;
overflow-x: auto;
}
</style>

@ -0,0 +1,226 @@
<template>
<el-container style="height: 100vh;">
<!-- 顶部标题 -->
<el-header style="background-color: #A8B9C9; color: #F2F8FE; text-align: center; font-size: 24px; padding: 20px;">
快递员中心
</el-header>
<el-container>
<!-- 左侧菜单栏 -->
<el-aside width="250px" style="background-color: #f4f6f9;">
<el-menu :default-active="currentTab" class="el-menu-vertical-demo" @select="handleTabChange">
<el-menu-item index="toDeliver">待配送订单</el-menu-item>
<el-menu-item index="history">历史订单</el-menu-item>
</el-menu>
</el-aside>
<!-- 主内容区域 -->
<el-main style="padding: 20px;">
<!-- 待配送订单 -->
<el-row v-if="currentTab === 'toDeliver'" class="order-card">
<h3>待配送订单</h3>
<div class="button-container">
<el-button type="primary" @click="openAddOrderDialog" class="btn">新增订单</el-button>
</div>
<el-table :data="toDeliverOrders" border stripe>
<el-table-column prop="id" label="订单编号" width="180" />
<el-table-column prop="receiver" label="收件人" width="180" />
<el-table-column prop="address" label="地址" />
<el-table-column prop="status" label="状态" width="120" />
<el-table-column label="操作" width="180">
<template #default="{ row }">
<el-button v-if="row.status === ''" type="primary" size="small"
@click="updateStatus(row, '配送中')">开始配送</el-button>
<el-button v-if="row.status === ''" type="success" size="small"
@click="updateStatus(row, '已完成')">完成配送</el-button>
</template>
</el-table-column>
</el-table>
</el-row>
<!-- 历史订单 -->
<el-row v-if="currentTab === 'history'" class="order-card">
<h3>历史订单</h3>
<el-table :data="historyOrders" border stripe>
<el-table-column prop="id" label="订单编号" width="180" />
<el-table-column prop="receiver" label="收件人" width="180" />
<el-table-column prop="address" label="地址" />
<el-table-column prop="status" label="状态" width="120" />
<el-table-column prop="deliveryTime" label="配送时间" width="180" />
<el-table-column label="操作" width="180">
<template #default="{ row }">
<el-button type="danger" size="small" @click="deleteHistoryOrder(row)"></el-button>
</template>
</el-table-column>
</el-table>
</el-row>
</el-main>
<!-- 新增订单对话框 -->
<el-dialog title="新增订单" v-model="addOrderDialogVisible" width="400px" align-center>
<el-form :model="newOrder" ref="newOrderForm" label-width="100px">
<el-form-item label="订单编号">
<el-input v-model="newOrder.id" placeholder="请输入订单编号" />
</el-form-item>
<el-form-item label="收件人">
<el-input v-model="newOrder.receiver" placeholder="请输入收件人" />
</el-form-item>
<el-form-item label="地址">
<el-input v-model="newOrder.address" placeholder="请输入地址" />
</el-form-item>
<el-form-item label="状态">
<el-select v-model="newOrder.status" placeholder="请选择状态">
<el-option label="待取件" value="待取件" />
<el-option label="配送中" value="配送中" />
<el-option label="已完成" value="已完成" />
</el-select>
</el-form-item>
</el-form>
<el-row slot="footer" class="dialog-footer">
<el-button @click="addOrderDialogVisible = false">取消</el-button>
<el-button type="primary" @click="addOrder"></el-button>
</el-row>
</el-dialog>
</el-container>
</el-container>
</template>
<script setup>
import {
ref
} from 'vue';
// State
const currentTab = ref('toDeliver');
const addOrderDialogVisible = ref(false); //
const newOrder = ref({
id: '',
receiver: '',
address: '',
status: '待取件'
});
const toDeliverOrders = ref([{
id: '001',
receiver: '李四',
address: '北京市朝阳区',
status: '待取件'
},
{
id: '002',
receiver: '王五',
address: '北京市海淀区',
status: '待取件'
},
{
id: '003',
receiver: '赵六',
address: '北京市昌平区',
status: '配送中'
}
]);
const historyOrders = ref([{
id: '004',
receiver: '孙七',
address: '北京市东城区',
status: '已完成',
deliveryTime: '2024-10-10 14:00'
},
{
id: '005',
receiver: '周八',
address: '北京市西城区',
status: '已完成',
deliveryTime: '2024-10-12 15:30'
}
]);
// Methods
const updateStatus = (order, newStatus) => {
const orderIndex = toDeliverOrders.value.findIndex(o => o.id === order.id);
if (orderIndex !== -1) {
toDeliverOrders.value[orderIndex].status = newStatus;
if (newStatus === '已完成') {
historyOrders.value.push({
...toDeliverOrders.value[orderIndex],
deliveryTime: new Date().toLocaleString()
});
toDeliverOrders.value.splice(orderIndex, 1);
}
}
};
const deleteHistoryOrder = (order) => {
const index = historyOrders.value.findIndex(o => o.id === order.id);
if (index !== -1) {
historyOrders.value.splice(index, 1);
}
};
const handleTabChange = (index) => {
currentTab.value = index;
};
const openAddOrderDialog = () => {
addOrderDialogVisible.value = true;
};
const addOrder = () => {
if (newOrder.value.id && newOrder.value.receiver && newOrder.value.address) {
toDeliverOrders.value.push({
...newOrder.value
});
newOrder.value = {
id: '',
receiver: '',
address: '',
status: '待取件'
}; //
addOrderDialogVisible.value = false; //
} else {
alert('请填写完整订单信息');
}
};
</script>
<style scoped>
.btn {
margin-left: 1200px;
}
.order-card {
margin-top: 20px;
}
.el-table th {
background-color: #f2f3f5;
text-align: center;
}
.el-table td {
text-align: center;
}
.el-menu-vertical-demo {
margin-top: 20px;
}
.el-menu-item {
font-size: 16px;
}
.el-button {
margin-top: 5px;
}
.dialog-footer {
text-align: right;
}
.button-container {
text-align: right;
margin-bottom: 10px;
}
</style>

@ -0,0 +1,181 @@
<template>
<el-container>
<el-header>
<Header />
</el-header>
<el-container>
<el-main>
<!-- 地址列表 -->
<el-row class="address-container">
<el-button type="primary" @click="showAddDialog"></el-button>
<el-table :data="addressList" style="width: 100%; margin-top: 20px;">
<el-table-column prop="name" label="联系人" width="180"></el-table-column>
<el-table-column prop="phone" label="电话" width="180"></el-table-column>
<el-table-column prop="fullAddress" label="地址"></el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button size="mini" @click="editAddress(scope.row)"></el-button>
<el-button size="mini" type="danger" @click="deleteAddress(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
</el-row>
</el-main>
</el-container>
<el-footer>
<Footer />
</el-footer>
<!-- 新增地址弹窗 -->
<el-dialog v-model="dialogVisible" :title="dialogTitle" align-center>
<el-form :model="newAddress" :rules="rules" ref="addressForm" label-width="120px">
<el-form-item label="联系人" prop="name">
<el-input v-model="newAddress.name" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="电话" prop="phone">
<el-input v-model="newAddress.phone" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="省市" prop="provinceCity">
<el-cascader v-model="newAddress.provinceCity" autocomplete="off" :options="provinceCityOptions" placeholder="选择省市"
:props="{ expandTrigger: 'hover' }" clearable />
</el-form-item>
<el-form-item label="详细地址" prop="address">
<el-input v-model="newAddress.address" autocomplete="off"></el-input>
</el-form-item>
</el-form>
<el-row class="dialog-footer">
<el-button @click="cancelDialog"></el-button>
<el-button type="primary" @click="saveAddress"></el-button>
</el-row>
</el-dialog>
</el-container>
</template>
<script setup>
import { reactive, ref, computed, watch } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import addressListJson from '../../assets/addressList.js';
//
const addressList = ref([
{ name: '陈子润', phone: '19502407201', fullAddress: '福建省福州市杨桥西路50号福州大学至诚学院' }
]);
//
const dialogVisible = ref(false);
//
const dialogTitle = ref('新增地址');
//
const editingIndex = ref(-1);
//
const newAddress = reactive({
name: '',
phone: '',
provinceCity: [],
address: ''
});
//
const formatCityData = (arr) => {
return arr.map(item => ({
value: item.name,
label: item.name,
children: item.sub && item.sub.length > 0 ? formatCityData(item.sub) : []
}));
};
//
const provinceCityOptions = computed(() => formatCityData(addressListJson));
//
const rules = {
name: [{ required: true, message: '请输入联系人', trigger: 'blur' }],
phone: [
{ required: true, message: '请输入电话号码', trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: '请输入有效的手机号', trigger: 'blur' }
],
address: [{ required: true, message: '请输入地址', trigger: 'blur' }]
};
//
const addressForm = ref();
//
const showAddDialog = () => {
dialogVisible.value = true;
};
//
const saveAddress = () => {
addressForm.value.validate((valid) => {
if (valid) {
const fullAddress = `${newAddress.provinceCity.join('')}${newAddress.address}`;
if (editingIndex.value === -1) {
//
addressList.value.push({ ...newAddress, fullAddress });
ElMessage.success('地址新增成功');
} else {
//
addressList.value[editingIndex.value] = { ...newAddress, fullAddress };
ElMessage.success('地址修改成功');
}
dialogVisible.value = false;
addressForm.value.resetFields();
}
});
};
//
const editAddress = (address) => {
dialogTitle.value = '编辑地址';
editingIndex.value = addressList.value.findIndex((item) => item === address);
newAddress.name = address.name;
newAddress.phone = address.phone;
newAddress.address = address.address;
newAddress.provinceCity = address.provinceCity || [];
dialogVisible.value = true;
};
//
const deleteAddress = (address) => {
ElMessageBox.confirm('确认删除该地址吗?', '提示', {
type: 'warning',
showCancelButton: true
}).then(() => {
addressList.value = addressList.value.filter((item) => item !== address);
ElMessage.success('地址删除成功');
});
};
//
const cancelDialog = () => {
dialogVisible.value = false;
};
//
watch(dialogVisible, (newValue) => {
if (!newValue) {
addressForm.value?.resetFields();
}
});
</script>
<style>
.address-container {
margin-top: 50px;
padding: 20px;
background-color: #f9f9f9;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
text-align: center;
}
.dialog-footer {
text-align: right;
}
</style>

@ -0,0 +1,406 @@
<template>
<el-container>
<!-- 导航栏 -->
<el-header>
<Header />
</el-header>
<el-main>
<div class="container">
<!-- 标题输入框和按钮 -->
<div class="top-section">
<h1 class="title">运单查询</h1>
<div class="input-group">
<el-input
v-model="form.trackingNumber"
placeholder="请输入快递单号"
clearable
style="width: 300px; margin-right: 20px;"
@input="validateTrackingNumber"
@keyup.enter="searchPackage"
></el-input>
<el-button @click="searchPackage" type="primary" :loading="loading">查询</el-button>
</div>
</div>
<!-- 选项卡 -->
<el-tabs v-model="activeTab" type="card" style="margin-top: 20px;" @tab-click="handleTabClick">
<el-tab-pane label="我寄的" name="sent">
<el-table v-if="sentPackages.length" :data="sentPackages" style="margin-top: 20px;">
<el-table-column label="快递单号" prop="trackingNumber"></el-table-column>
<el-table-column label="包裹名称" prop="packageName"></el-table-column>
<el-table-column label="寄件人" prop="sender"></el-table-column>
<el-table-column label="寄件人地址" prop="senderaddress"></el-table-column>
<el-table-column label="收件人" prop="receiver"></el-table-column>
<el-table-column label="收件人地址" prop="receiveraddress"></el-table-column>
<el-table-column label="状态" prop="status">
<template #default="scope">
<span :class="getStatusClass(scope.row.status)">{{ scope.row.status }}</span>
</template>
</el-table-column>
<el-table-column label="最新位置" prop="latestLocation"></el-table-column>
</el-table>
<p v-else></p>
</el-tab-pane>
<el-tab-pane label="我收的" name="received">
<el-table v-if="receivedPackages.length" :data="receivedPackages" style="margin-top: 20px;">
<el-table-column label="快递单号" prop="trackingNumber"></el-table-column>
<el-table-column label="包裹名称" prop="packageName"></el-table-column>
<el-table-column label="寄件人地址" prop="senderaddress"></el-table-column>
<el-table-column label="寄件人" prop="sender"></el-table-column>
<el-table-column label="收件人地址" prop="receiveraddress"></el-table-column>
<el-table-column label="收件人" prop="receiver"></el-table-column>
<el-table-column label="状态" prop="status">
<template #default="scope">
<span :class="getStatusClass(scope.row.status)">{{ scope.row.status }}</span>
</template>
</el-table-column>
<el-table-column label="最新位置" prop="latestLocation"></el-table-column>
</el-table>
<p v-else></p>
</el-tab-pane>
</el-tabs>
</div>
</el-main>
<el-footer>
<Footer />
</el-footer>
</el-container>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue'
import { ElMessage, ElMessageBox, ElInput, ElButton, ElTable, ElTableColumn } from 'element-plus'
//
interface Package {
trackingNumber: string;
packageName: string;
sender: string;
senderaddress: string;
receiver: string;
receiveraddress: string;
status: string;
latestLocation: string;
}
//
const mockData: Package[] = [
{
trackingNumber: '123',
packageName: '书籍',
sender: '张帅齐',
senderaddress: '北京市海淀区',
receiver: '李四',
receiveraddress: '上海市浦东新区',
status: '未签收',
latestLocation: '北京市海淀区',
},
{
trackingNumber: '456',
packageName: '衣物',
sender: '林涛',
senderaddress: '上海市浦东新区',
receiver: '李四',
receiveraddress: '北京市海淀区',
status: '正在运输',
latestLocation: '上海市浦东新区',
},
{
trackingNumber: '789',
packageName: '电子产品',
sender: '陈子润',
senderaddress: '北京市海淀区',
receiver: '李四',
receiveraddress: '北京市海淀区',
status: '已签收',
latestLocation: '北京市海淀区',
},
{
trackingNumber: '011',
packageName: '书籍',
sender: '张帅齐',
senderaddress: '北京市海淀区',
receiver: '李四',
receiveraddress: '北京市海淀区',
status: '已签收',
latestLocation: '北京市海淀区',
},
{
trackingNumber: '012',
packageName: '书籍',
sender: '孙贺贺',
senderaddress: '北京市海淀区',
receiver: '李四',
receiveraddress: '北京市海淀区',
status: '已签收',
latestLocation: '北京市海淀区',
},
{
trackingNumber: '013',
packageName: '书籍',
sender: '梁佳琪',
senderaddress: '北京市海淀区',
receiver: '李四',
receiveraddress: '北京市海淀区',
status: '未签收',
latestLocation: '北京市海淀区',
}
]
const form = ref({
trackingNumber: ''
})
const activeTab = ref('sent')
const sentPackages = ref<Package[]>([]) //
const receivedPackages = ref<Package[]>([]) //
const error = ref('')
const loading = ref(false)
//
const validateTrackingNumber = () => {
if (form.value.trackingNumber && !/^\d+$/.test(form.value.trackingNumber)) {
error.value = '快递单号只能包含数字'
} else {
error.value = ''
}
}
//
const handleTabClick = (tab: any) => {
//
sentPackages.value = [];
receivedPackages.value = [];
if (tab.paneName === 'sent') {
sentPackages.value = mockData.filter(item => ['123', '456', '789'].includes(item.trackingNumber));
activeTab.value = 'sent'; // activeTab
} else if (tab.paneName === 'received') {
receivedPackages.value = mockData.filter(item => ['011', '012', '013'].includes(item.trackingNumber));
activeTab.value = 'received'; // activeTab
}
}
//
const initSentPackages = () => {
sentPackages.value = mockData.filter(item => ['123', '456', '789'].includes(item.trackingNumber));
}
//
onMounted(() => {
initSentPackages();
})
//
const searchPackage = async () => {
if (!form.value.trackingNumber) {
ElMessage.error('请输入快递单号')
return
}
if (error.value) {
ElMessageBox.alert(error.value, '错误', {
confirmButtonText: '确定',
type: 'error',
})
return
}
loading.value = true
try {
const result = mockData.find(item => item.trackingNumber === form.value.trackingNumber)
if (result) {
ElMessage.success('查询成功')
//
if (['123', '456', '789'].includes(result.trackingNumber)) {
activeTab.value = 'sent'
sentPackages.value = [result] //
receivedPackages.value = [] //
} else if (['011', '012', '013'].includes(result.trackingNumber)) {
activeTab.value = 'received'
receivedPackages.value = [result] //
sentPackages.value = [] //
}
} else {
ElMessageBox.alert('未找到该快递单号的信息,请确认单号是否正确。', '错误', {
confirmButtonText: '确定',
type: 'error',
})
}
} finally {
loading.value = false
}
}
//
const getStatusClass = (status: string) => {
switch (status) {
case '已签收':
return 'status-success';
case '正在运输':
return 'status-warning';
default:
return 'status-fail';
}
}
</script>
<style scoped>
/* 将容器设置为 flex 布局,居中对齐 */
.container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start; /* 从顶部开始排列 */
padding: 20px;
/* background: #f5f5f5; */
}
/* 标题样式 */
.title {
font-size: 32px;
font-weight: bold;
color: #333;
margin-bottom: 20px;
text-align: center;
margin-top: 0px; /* 为标题留出导航栏的空间 */
}
/* 顶部区域样式 */
.top-section {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
max-width: 600px;
}
/* 输入框和按钮容器 */
.input-group {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 20px;
}
.input-group .el-input {
margin-right: 20px;
width: 300px;
}
.el-button {
padding: 10px 20px;
font-size: 16px;
}
/* 表格样式 */
.el-table {
width: 100%;
max-width: 1000px; /* 增加表格的最大宽度 */
margin-top: 20px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
border-radius: 10px;
overflow: hidden;
}
.el-table th,
.el-table td {
text-align: center;
padding: 12px 0;
}
.el-table th {
background: #f0f0f0;
font-weight: bold;
}
.el-table tr:nth-child(even) {
background: #f9f9f9;
}
.el-table tr:hover {
background: #e0e0e0;
}
/* 状态颜色样式 */
.status-success {
color: green;
}
.status-fail {
color: rgb(255, 0, 0);
}
.status-warning {
color: orange;
}
/* 选项卡样式 */
.el-tabs {
width: 100%;
max-width: 1000px; /* 增加选项卡的最大宽度 */
margin-top: 20px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
border-radius: 10px;
overflow: hidden;
}
.el-tabs__header {
margin: 0;
padding: 0 20px;
background: #f0f0f0;
border-bottom: 1px solid #ddd;
}
.el-tabs__nav-wrap::after {
display: none;
}
.el-tabs__item {
padding: 0 20px;
height: 40px;
line-height: 40px;
font-weight: bold;
color: #333;
transition: background 0.3s, color 0.3s, border-bottom 0.3s;
border-bottom: 2px solid transparent; /* 默认情况下没有下划线 */
}
.el-tabs__item.is-active {
background: #fff;
color: #409eff;
border-bottom: 2px solid red !important; /* 激活状态下有红色下划线 */
}
.el-tabs__item:hover {
background: #e0e0e0;
color: #409eff;
border-bottom: 2px solid #f60d0d !important; /* 悬停时有下划线 */
}
/* Element UI 容器样式 */
.el-header {
padding: 0;
height: 100;
}
.el-main {
margin-top: 80px;
padding: 20px 0;
overflow: hidden;
height: calc(100vh - 200px); /* 调整高度以适应屏幕 */
}
.el-footer {
margin-top: 0px;
padding: 20px 0;
/* background: #333; */
color: #fff;
text-align: center;
}
</style>

@ -0,0 +1,125 @@
<template>
<el-container>
<el-header>
<Header />
</el-header>
<el-container>
<el-main>
<div class="complaint-container">
<h2>快递投诉</h2>
<el-form
:model="complaintForm"
:rules="rules"
ref="complaintFormRef"
label-width="100px"
class="complaint-form"
>
<el-form-item label="订单号" prop="orderNumber">
<el-input v-model="complaintForm.orderNumber" placeholder="请输入订单号" />
</el-form-item>
<el-form-item label="投诉原因" prop="reason">
<el-select v-model="complaintForm.reason" placeholder="请选择投诉原因">
<el-option label="快递未按时送达" value="未按时送达" />
<el-option label="快递损坏或丢失" value="损坏或丢失" />
<el-option label="服务态度差" value="服务态度差" />
<el-option label="其他问题" value="其他" />
</el-select>
</el-form-item>
<el-form-item label="详细描述" prop="description">
<el-input
type="textarea"
v-model="complaintForm.description"
placeholder="请详细描述您的问题"
rows="4"
/>
</el-form-item>
<el-form-item label="联系方式" prop="contact">
<el-input v-model="complaintForm.contact" placeholder="请输入您的联系方式" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitComplaint"></el-button>
<el-button @click="resetForm"></el-button>
</el-form-item>
</el-form>
</div>
</el-main>
</el-container>
<el-footer>
<Footer />
</el-footer>
</el-container>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { ElMessage } from 'element-plus';
const complaintForm = ref({
orderNumber: '',
reason: '',
description: '',
contact: '',
});
const rules = {
orderNumber: [
{ required: true, message: '订单号不能为空', trigger: 'blur' },
],
reason: [
{ required: true, message: '请选择投诉原因', trigger: 'change' },
],
description: [
{ required: true, message: '请详细描述问题', trigger: 'blur' },
{ min: 10, message: '描述至少需要10个字', trigger: 'blur' },
],
contact: [
{ required: true, message: '联系方式不能为空', trigger: 'blur' },
{
pattern: /^1[3-9]\d{9}$/,
message: '请输入有效的手机号码',
trigger: 'blur',
},
],
};
const complaintFormRef = ref();
const submitComplaint = () => {
complaintFormRef.value?.validate((valid) => {
if (valid) {
ElMessage.success('投诉已提交,我们将尽快处理您的问题!');
resetForm();
} else {
ElMessage.error('请检查表单内容是否填写完整!');
}
});
};
const resetForm = () => {
complaintFormRef.value?.resetFields();
};
</script>
<style scoped>
.complaint-container {
max-width: 600px;
margin: 20px auto;
padding: 20px;
border: 1px solid #e0e0e0;
border-radius: 8px;
background-color: #fff;
}
h2 {
text-align: center;
margin-bottom: 20px;
}
.complaint-form {
width: 100%;
}
</style>

@ -0,0 +1,289 @@
<template>
<el-container>
<el-header>
<Header />
</el-header>
<el-container>
<el-main>
<el-row class="send-container">
<el-row class="panel send-panel send-panel1">
<el-row class="send-container">
<el-button class="send-btn"></el-button>
<el-row>
<el-button @click="openAddressBook">簿</el-button>
<el-button>智能填写</el-button>
</el-row>
</el-row>
<el-form ref="senderFormRef" label-width="auto" class="form" :model="form.sender" :rules="rules.sender">
<el-form-item label="姓名" prop="senderName">
<el-input v-model="form.sender.senderName" placeholder="请填写发件人姓名"></el-input>
</el-form-item>
<el-form-item label="联系电话" prop="senderPhone">
<el-input v-model="form.sender.senderPhone" placeholder="请填写发件人联系电话"></el-input>
</el-form-item>
<el-form-item label="省市区" prop="senderProvince">
<el-cascader
:options="options"
v-model="form.sender.senderProvince"
:props="{ value: 'value', label: 'label', children: 'children' }"
placeholder="请选择省市区"
/>
</el-form-item>
<el-form-item label="详细地址" prop="senderAddress">
<el-input v-model="form.sender.senderAddress" placeholder="请填写详细地址"></el-input>
</el-form-item>
<el-form-item label="公司名称" prop="senderCompany" class="no-asterisk">
<el-input v-model="form.sender.senderCompany" placeholder="请填写公司名称"></el-input>
</el-form-item>
</el-form>
</el-row>
<el-image src="src/assets/arrow.png"></el-image>
<el-row class="panel send-panel send-panel1">
<el-row class="send-container">
<el-button class="send-btn receiver-btn"></el-button>
<el-row>
<el-button @click="openAddressBook">簿</el-button>
<el-button>智能填写</el-button>
</el-row>
</el-row>
<el-form ref="receiverFormRef" label-width="auto" class="form" :model="form.receiver" :rules="rules.receiver">
<el-form-item label="姓名" prop="receiverName">
<el-input v-model="form.receiver.receiverName" placeholder="请填写收件人姓名"></el-input>
</el-form-item>
<el-form-item label="联系电话" prop="receiverPhone">
<el-input v-model="form.receiver.receiverPhone" placeholder="请填写收件人联系电话"></el-input>
</el-form-item>
<el-form-item label="省市区" prop="receiverProvince">
<el-cascader
:options="options"
v-model="form.receiver.receiverProvince"
:props="{ value: 'value', label: 'label', children: 'children' }"
placeholder="请选择省市区"
/>
</el-form-item>
<el-form-item label="详细地址" prop="receiverAddress">
<el-input v-model="form.receiver.receiverAddress" placeholder="请填写详细地址"></el-input>
</el-form-item>
<el-form-item label="公司名称" prop="receiverCompany" class="no-asterisk">
<el-input v-model="form.receiver.receiverCompany" placeholder="请填写公司名称"></el-input>
</el-form-item>
</el-form>
</el-row>
<el-row class="panel send-panel">
<el-text class="end-label">寄件方式</el-text>
<el-radio-group v-model="sendType">
<el-radio :value="0" size="large">预约上门取件</el-radio>
<el-radio :value="1" size="large">自行联系快递员</el-radio>
</el-radio-group>
</el-row>
<el-row class="panel send-panel">
<el-button class="confirm-btn" @click="submitForm"></el-button>
</el-row>
</el-row>
</el-main>
</el-container>
<el-footer style="flex: none;">
<Footer />
</el-footer>
</el-container>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { useRouter } from 'vue-router'; //
import addressList from '../../assets/addressList.js';
const formatCityData = (arr) => {
return arr.map(item => ({
value: item.name,
label: item.name,
children: item.sub && item.sub.length > 0 ? formatCityData(item.sub) : []
}));
};
const options = ref(formatCityData(addressList));
const sendType = ref(0);
//
const router = useRouter();
// 簿address.vue
const openAddressBook = () => {
router.push({ name: 'address' }); // address.vue'/address'
};
const form = ref({
sender: {
senderName: '',
senderPhone: '',
senderProvince: [],
senderAddress: '',
senderCompany: ''
},
receiver: {
receiverName: '',
receiverPhone: '',
receiverProvince: [],
receiverAddress: '',
receiverCompany: ''
}
});
const rules = ref({
receiver: {
receiverName: [
{ required: true, message: '请输入姓名', trigger: 'blur' }
],
receiverPhone: [
{ required: true, message: '请输入联系电话', trigger: 'blur' }
],
receiverProvince: [
{ required: true, message: '请输入省市区', trigger: 'blur' }
],
receiverAddress: [
{ required: true, message: '请输入详细地址', trigger: 'blur' }
],
receiverCompany: [
{ required: false, message: '请输入公司名称', trigger: 'blur' }
],
},
sender: {
senderName: [
{ required: true, message: '请输入姓名', trigger: 'blur' }
],
senderPhone: [
{ required: true, message: '请输入联系电话', trigger: 'blur' }
],
senderProvince: [
{ required: true, message: '请输入省市区', trigger: 'blur' }
],
senderAddress: [
{ required: true, message: '请输入详细地址', trigger: 'blur' }
],
senderCompany: [
{ required: false, message: '请输入公司名称', trigger: 'blur' }
],
}
});
const senderFormRef = ref(null);
const receiverFormRef = ref(null);
const submitForm = () => {
// receiverFormRef.value.validate
if (senderFormRef.value) {
senderFormRef.value.validate((valid) => {
if (valid) {
alert('发件人表单提交成功!');
//
} else {
console.log('发件人表单验证失败!');
return false;
}
});
}
// receiverFormRef.value.validate
if (receiverFormRef.value) {
receiverFormRef.value.validate((valid) => {
if (valid) {
alert('收件人表单提交成功!');
//
} else {
console.log('收件人表单验证失败!');
return false;
}
});
}
};
</script>
<style>
.container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start; /* 从顶部开始排列 */
padding: 20px;
/* background: #f5f5f5; */
}
.send-container {
display: flex;
width: 70%;
flex-direction: row;
justify-content: space-between;
margin-top: 10px;
align-items: center;
margin-left: auto;
margin-right: auto; /* 添加这一行 */
}
/* 卡片容器样式 */
.panel {
background-color: #f9f9f9; /* 淡雅的背景颜色 */
border-radius: 8px; /* 圆角边框 */
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); /* 添加阴影效果 */
padding: 30px;
margin-top: 30px;
margin-bottom: 20px;
width: calc(50% - 20px); /* 设置宽度为容器宽度的一半减去间距 */
}
/* 调整第二个卡片的样式 */
.send-panel1 {
width: calc(50% - 20px); /* 设置宽度为容器宽度的一半减去间距 */
align-items: center;
}
/* 按钮样式 */
.send-btn {
background-color: #333; /* 深灰色背景 */
color: white;
border-radius: 50%;
padding: 10px;
margin-right: 10px; /* 添加右边距 */
}
.receiver-btn {
background-color: #d9534f; /* 红色背景 */
}
/* 表单样式 */
.form {
margin-top: 10px;
width: 70%; /* 表单宽度调整为100% */
}
/* 确认按钮样式 */
.confirm-btn {
background-color: #428bca; /* 蓝色背景 */
color: white;
padding: 10px 20px; /* 添加内边距 */
border: none; /* 去除边框 */
border-radius: 5px; /* 圆角边框 */
margin-top: 5px;
margin-bottom: 5px;
cursor: pointer; /* 鼠标悬停时显示指针 */
}
/* 标签样式 */
.el-form-item__label {
color: #333; /* 标签文字颜色 */
font-weight: 500; /* 字体加粗 */
}
.end-label {
font-weight: 600;
align-self: flex-start;
margin-right: 40px;
margin-top: 10px;
}
.el-form-item__label::before {
content: '*';
color: red;
margin-right: 4px;
}
.el-form-item.no-asterisk .el-form-item__label::before {
content: none;
}
</style>

@ -0,0 +1,122 @@
<template>
<el-container>
<el-header>
<Header />
</el-header>
<el-container>
<el-main>
<div class="feedback-container">
<h2>我有建议</h2>
<p class="feedback-description">
如果您对我们的服务或功能有任何建议请告诉我们我们会认真听取并改进
</p>
<el-form
:model="feedbackForm"
:rules="rules"
ref="feedbackFormRef"
label-width="100px"
class="feedback-form"
>
<el-form-item label="标题" prop="title">
<el-input
v-model="feedbackForm.title"
placeholder="请输入您的建议标题"
/>
</el-form-item>
<el-form-item label="建议内容" prop="content">
<el-input
type="textarea"
v-model="feedbackForm.content"
placeholder="请输入详细建议内容"
rows="5"
/>
</el-form-item>
<el-form-item label="联系方式" prop="contact">
<el-input
v-model="feedbackForm.contact"
placeholder="请输入您的联系方式(可选)"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitFeedback">
提交建议
</el-button>
<el-button @click="resetForm"></el-button>
</el-form-item>
</el-form>
</div>
</el-main>
</el-container>
<el-footer>
<Footer />
</el-footer>
</el-container>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { ElMessage } from 'element-plus';
const feedbackForm = ref({
title: '',
content: '',
contact: '',
});
const rules = {
title: [
{ required: true, message: '标题不能为空', trigger: 'blur' },
{ min: 3, message: '标题至少需要3个字符', trigger: 'blur' },
],
content: [
{ required: true, message: '建议内容不能为空', trigger: 'blur' },
{ min: 10, message: '内容至少需要10个字符', trigger: 'blur' },
],
};
const feedbackFormRef = ref();
const submitFeedback = () => {
feedbackFormRef.value?.validate((valid) => {
if (valid) {
ElMessage.success('感谢您的建议!我们会认真参考并改进。');
resetForm();
} else {
ElMessage.error('请填写完整的建议内容!');
}
});
};
const resetForm = () => {
feedbackFormRef.value?.resetFields();
};
</script>
<style scoped>
.feedback-container {
max-width: 600px;
margin: 20px auto;
padding: 20px;
border: 1px solid #e0e0e0;
border-radius: 8px;
background-color: #fff;
}
h2 {
text-align: center;
margin-bottom: 10px;
}
.feedback-description {
text-align: center;
margin-bottom: 20px;
color: #666;
}
.feedback-form {
width: 100%;
}
</style>

@ -0,0 +1,300 @@
<template>
<el-container style="height: 100vh;">
<!-- 页面头部 -->
<el-header>
<Header />
</el-header>
<el-container>
<!-- 页面主体 -->
<el-main>
<div class="profile-container">
<!-- 用户资料表单 -->
<el-card class="profile-card" shadow="hover">
<div class="profile-header">
<!-- 头像 -->
<el-avatar
style="width: 80px; height: 80px;"
:src="avatarUrl"
class="avatar"
@click="changeAvatar"
/>
<div class="profile-info">
<el-form ref="profileForm" :model="profileInfo" label-width="80px">
<el-form-item label="用户名">
<el-input v-model="profileInfo.name" placeholder="请输入用户名"></el-input>
</el-form-item>
<el-form-item label="邮箱">
<el-input v-model="profileInfo.email" placeholder="请输入邮箱"></el-input>
</el-form-item>
</el-form>
</div>
</div>
<el-button type="danger" @click="logout">退</el-button>
<!-- 用户详细信息表单 -->
<div class="profile-details">
<el-form ref="profileDetailsForm" :model="profileInfo" label-width="80px">
<el-row>
<el-col :span="12">
<el-form-item label="手机号">
<el-input v-model="profileInfo.phone" placeholder="请输入手机号"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="性别">
<el-select v-model="profileInfo.gender" placeholder="请选择性别">
<el-option label="男" value="男"></el-option>
<el-option label="女" value="女"></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="出生日期">
<el-date-picker v-model="profileInfo.birthday" type="date" placeholder="选择日期"></el-date-picker>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="地址">
<el-input v-model="profileInfo.address" placeholder="请输入地址"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
</el-card>
<!-- 地址管理表单 -->
<el-card class="address-card" shadow="hover" style="margin-top: 20px;">
<h3>地址管理</h3>
<el-button type="primary" @click="addAddress"></el-button>
<!-- 地址表单 -->
<el-form :model="addresses" label-width="80px" style="margin-top: 20px;">
<div v-for="(address, index) in addresses" :key="address.id" class="address-item">
<el-form-item label="收货人" :prop="'name' + index">
<el-input v-model="address.name" placeholder="请输入收货人姓名"></el-input>
</el-form-item>
<el-form-item label="地址" :prop="'address' + index">
<el-input v-model="address.address" placeholder="请输入收货地址"></el-input>
</el-form-item>
<el-form-item label="电话" :prop="'phone' + index">
<el-input v-model="address.phone" placeholder="请输入联系电话"></el-input>
</el-form-item>
<!-- 复制和编辑操作 -->
<el-form-item>
<el-button type="text" @click="editAddress(index)"></el-button>
<el-button type="text" @click="deleteAddress(address.id)"></el-button>
<el-button type="text" @click="copyAddress(address)"></el-button>
</el-form-item>
</div>
</el-form>
</el-card>
</div>
</el-main>
</el-container>
<!-- 页面底部 -->
<el-footer>
<Footer />
</el-footer>
</el-container>
</template>
<script lang="ts" setup>
import { ElMessage } from 'element-plus';
import { onMounted, ref } from 'vue';
import router from '../../router';
interface Address {
id: number;
name: string;
address: string;
phone: string;
}
//
const profileInfo = ref({
name: '张三',
email: 'example@mail.com',
phone: '13800000000',
gender: '男',
birthday: '1990-01-01',
address: '北京市朝阳区某某街道',
});
// localStorage
const loadAddresses = () => {
const savedData = localStorage.getItem('addresses');
return savedData ? JSON.parse(savedData) : [];
};
// localStorage
const saveAddresses = () => {
localStorage.setItem('addresses', JSON.stringify(addresses.value));
};
//
const addresses = ref<Address[]>([]);
// URL
const avatarUrl = ref<string>('src/assets/user.png'); //
//
const changeAvatar = () => {
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.accept = 'image/*';
fileInput.onchange = (event) => {
const file = event.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = (e) => {
avatarUrl.value = e.target.result; // URL
};
reader.readAsDataURL(file); // DataURL
}
};
fileInput.click(); //
};
//
const backupAddress = ref<Address | null>(null);
//
onMounted(() => {
addresses.value = loadAddresses(); // localStorage
});
//
const backupCurrentAddress = (index: number) => {
backupAddress.value = { ...addresses.value[index] };
};
//
const addAddress = () => {
const newId = addresses.value.length ? Math.max(...addresses.value.map(addr => addr.id)) + 1 : 1;
const newAddress = { id: newId, name: '', address: '', phone: '' };
addresses.value.push(newAddress);
saveAddresses(); // localStorage
};
//
const editAddress = (index: number) => {
backupCurrentAddress(index); //
//
const isConfirmed = confirm('确定要保存更改吗?');
if (isConfirmed) {
//
console.log('保存地址:', addresses.value[index]);
alert('保存成功');
saveAddresses(); // localStorage
} else {
//
if (backupAddress.value) {
addresses.value[index] = { ...backupAddress.value };
}
console.log('修改已取消');
window.location.reload();
}
};
//
const deleteAddress = (id: number) => {
const index = addresses.value.findIndex((item) => item.id === id);
if (index !== -1) {
addresses.value.splice(index, 1);
saveAddresses(); // localStorage
}
};
const copyAddress = (address) => {
const textToCopy = `收货人: ${address.name}\n地址: ${address.address}\n电话: ${address.phone}`;
const textArea = document.createElement('textarea');
textArea.value = textToCopy;
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
// 使Element Plus
ElMessage.success('地址已复制到剪贴板');
};
const logout = () => {
router.push({ name: 'login' });
}
</script>
<style scoped>
.profile-container {
padding: 20px;
}
.profile-card {
padding: 20px;
}
.profile-header {
display: flex;
align-items: center;
}
.avatar-uploader {
position: relative;
cursor: pointer;
}
.avatar {
border-radius: 50%;
}
.profile-header .el-avatar {
margin-right: 20px;
}
.profile-info {
flex-grow: 1;
}
.username {
margin: 0;
font-size: 24px;
}
.email {
margin: 5px 0;
color: #888;
}
.edit-btn {
margin-left: auto;
}
.profile-details {
margin-top: 20px;
}
.address-card {
padding: 20px;
}
.address-btn {
margin-bottom: 10px;
}
.add-address-btn {
margin-top: 10px;
}
</style>

@ -0,0 +1,133 @@
<template>
<el-container>
<el-header>
<Header />
</el-header>
<el-main>
<div class="service-list-container">
<ul class="service-list">
<li>
<el-image src="src/assets/time.png"></el-image>
<div>
<p>运费时效</p>
<p>查预估费用和预计送达时间</p>
</div>
</li>
<li>
<el-image src="src/assets/serviceSpot.png"></el-image>
<div>
<p>服务网点</p>
<p>查各地网点丰巢柜合作点</p>
</div>
</li>
<li>
<el-image src="src/assets/require.png"></el-image>
<div>
<p>收寄标准</p>
<p>查物品能不能寄</p>
</div>
</li>
<li>
<el-image src="src/assets/serviceRange.png"></el-image>
<div>
<p>服务范围</p>
<p>查地址能不能收派</p>
</div>
</li>
</ul>
<ul class="service-list">
<li>
<el-icon><DataLine /></el-icon>
<div>
<p>汇率查询</p>
</div>
</li>
<li>
<el-icon><Document /></el-icon>
<div>
<p>常用表格</p>
</div>
</li>
<li>
<el-icon><SetUp /></el-icon>
<div>
<p>清关服务</p>
</div>
</li>
<li>
<el-icon><DataBoard /></el-icon>
<div>
<p>无着件公示</p>
</div>
</li>
<li>
<el-icon><HomeFilled /></el-icon>
<div>
<p>特殊入仓地址查询</p>
</div>
</li>
<li>
<el-icon><Download /></el-icon>
<div>
<p>电子回单下载</p>
</div>
</li>
</ul>
</div>
</el-main>
<el-footer>
<Footer />
</el-footer>
</el-container>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
</script>
<style scoped>
.service-list-container {
padding: 20px;
margin-top: 30px;
}
.service-title {
font-size: 18px;
font-weight: bold;
margin-bottom: 10px;
}
.service-list {
list-style: none;
padding: 0;
display: flex;
flex-wrap: wrap;
gap: 20px;
}
.service-list li {
flex: 1;
min-width: 200px;
border: 1px solid #eaeaea;
border-radius: 8px;
overflow: hidden;
text-align: center;
}
.service-list img {
width: 100%;
height: auto;
}
.service-list div {
padding: 10px;
}
.service-list p {
margin: 5px 0;
}
.el-icon{
font-size: 30px;
margin-top: 10px;
}
</style>

@ -0,0 +1,12 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
}
}
})
Loading…
Cancel
Save