博主介绍:专注于Java(springboot ssm springcloud等开发框架) vue .net php phython node.js uniapp小程序 等诸多技术领域和毕业项目实战、企业信息化系统建设,从业十五余年开发设计教学工作
☆☆☆ 精彩专栏推荐订阅☆☆☆☆☆不然下次找不到哟
我的博客空间发布了1000+毕设题目 方便大家学习使用
感兴趣的可以先收藏起来,还有大家在毕设选题,项目以及论文编写等相关问题都可以给我留言咨询,希望帮助更多的人
更多项目地址 介绍 翰文编程-CSDN博客
系统实现预览
系统设计
4.1系统通用功能用例分析
系统的通用功能包括用户登录和密码修改,是三个角色共同需要使用的功能,用例分析如图4-1所示。
图4-1系统通用功能用例分析图
4.2 系统设计主要功能
本系统采用自上往下的方法开发与实现,本课题要求实现一套山西文旅网,系统主要包括管理员模块和运营商、用户模块的功能模块;
(1)运营商用例图如下所示:
图4-2运营商用例图
(2)用户用例图如下所示:
图4-3用户用例图
(2)管理员用例图如下所示:
图4-4管理员用例图
通过市场调研及咨询研究,可以按照用户的角色权限使不同用户角色看到不一样的信息界面。现根据需求阶段的分析,我们可以大致确定系统需要包含的功能如下图4-5所示:
图4-5山西文旅网结构功能图
4.3 数据库设计
4.3.1 数据库设计规范
数据可设计要遵循职责分离原则,即在设计时应该要考虑系统独立性,即每个系统之间互不干预不能混乱数据表和系统关系。
数据库命名也要遵循一定规范,否则容易混淆,数据库字段名要尽量做到与表名类似。
4.3.2 E-R图
用户信息E-R图,如图4-6所示:
图4-6用户信息E-R图
运营商E-R图,如图4-7所示:
图4-7运营商E-R图
景点信息E-R图如图4-8所示。
图4-8景点信息E-R图
酒店信息E-R图如图4-9所示。
图4-9酒店信息E-R图
山西文旅网总体E-R图如图4-10所示。
图4-10山西文旅网总体E-R图
4.3.3 数据表
本系统采用的是MySQL数据库存储数据,系统中使用到的主要数据表的具体展示部分如下所示。
表4-1:我的收藏
字段名称 | 类型 | 长度 | 字段说明 | 主键 | 默认值 |
id | bigint | 主键 | 主键 | ||
addtime | timestamp | 创建时间 | CURRENT_TIMESTAMP | ||
refid | bigint | refid | |||
tablename | varchar | 200 | 表名 | ||
name | varchar | 200 | 名称 | ||
picture | longtext | 4294967295 | 图片 | ||
type | varchar | 200 | 类型(1:收藏,21:赞,22:踩,31:竞拍参与,41:关注) | ||
inteltype | varchar | 200 | 推荐类型 | ||
remark | varchar | 200 | 备注 | ||
userid | bigint | 用户id |
表4-2:用户
字段名称 | 类型 | 长度 | 字段说明 | 主键 | 默认值 |
id | bigint | 主键 | 主键 | ||
addtime | timestamp | 创建时间 | CURRENT_TIMESTAMP | ||
yonghuzhanghao | varchar | 200 | 用户账号 | ||
yonghumima | varchar | 200 | 用户密码 | ||
yonghuxingming | varchar | 200 | 用户姓名 | ||
touxiang | longtext | 4294967295 | 头像 | ||
xingbie | varchar | 200 | 性别 | ||
shoujihaoma | varchar | 200 | 手机号码 |
表4-3:景点信息评论表
字段名称 | 类型 | 长度 | 字段说明 | 主键 | 默认值 |
id | bigint | 主键 | 主键 | ||
addtime | timestamp | 创建时间 | CURRENT_TIMESTAMP | ||
refid | bigint | 关联表id | |||
userid | bigint | 用户id | |||
avatarurl | longtext | 4294967295 | 头像 | ||
nickname | varchar | 200 | 用户名 | ||
content | longtext | 4294967295 | 评论内容 | ||
reply | longtext | 4294967295 | 回复内容 |
表4-4:菜单
字段名称 | 类型 | 长度 | 字段说明 | 主键 | 默认值 |
id | bigint | 主键 | 主键 | ||
addtime | timestamp | 创建时间 | CURRENT_TIMESTAMP | ||
menujson | longtext | 4294967295 | 菜单 |
表4-5:管理员
字段名称 | 类型 | 长度 | 字段说明 | 主键 | 默认值 |
id | bigint | 主键 | 主键 | ||
addtime | timestamp | 创建时间 | CURRENT_TIMESTAMP | ||
username | varchar | 200 | 用户名 | ||
password | varchar | 200 | 密码 | ||
role | varchar | 200 | 角色 |
表4-6:配置文件
字段名称 | 类型 | 长度 | 字段说明 | 主键 | 默认值 |
id | bigint | 主键 | 主键 | ||
name | varchar | 100 | 配置参数名称 | ||
value | varchar | 100 | 配置参数值 |
表4-7:门票购买
字段名称 | 类型 | 长度 | 字段说明 | 主键 | 默认值 |
id | bigint | 主键 | 主键 | ||
addtime | timestamp | 创建时间 | CURRENT_TIMESTAMP | ||
dingdanbianhao | varchar | 200 | 订单编号 | ||
jingdianmingcheng | varchar | 200 | 景点名称 | ||
menpiaojiage | double | 门票价格 | |||
shuliang | int | 数量 | |||
dingdanjine | double | 订单金额 | |||
goumaishijian | datetime | 购买时间 | |||
yonghuzhanghao | varchar | 200 | 用户账号 | ||
yonghuxingming | varchar | 200 | 用户姓名 | ||
shoujihaoma | varchar | 200 | 手机号码 | ||
ispay | varchar | 200 | 是否支付 | ||
yunyingshangzhanghao | varchar | 200 | 运营商账号 | ||
yunyingshangxingming | varchar | 200 | 运营商姓名 |
表4-8:旅游攻略
字段名称 | 类型 | 长度 | 字段说明 | 主键 | 默认值 |
id | bigint | 主键 | 主键 | ||
addtime | timestamp | 创建时间 | CURRENT_TIMESTAMP | ||
gonglvemingcheng | varchar | 200 | 攻略名称 | ||
gonglveleixing | varchar | 200 | 攻略类型 | ||
tupian | longtext | 4294967295 | 图片 | ||
qidian | varchar | 200 | 起点 | ||
tujingluduan | varchar | 200 | 途径路段 | ||
mudedi | varchar | 200 | 目的地 | ||
chuxingfangshi | varchar | 200 | 出行方式 | ||
meishituijian | longtext | 4294967295 | 美食推荐 | ||
jingdiantuijian | longtext | 4294967295 | 景点推荐 | ||
jiudiantuijian | longtext | 4294967295 | 酒店推荐 | ||
gonglvexiangqing | longtext | 4294967295 | 攻略详情 | ||
youwantianshu | varchar | 200 | 游玩天数 | ||
yujifeiyong | varchar | 200 | 预计费用 | ||
yonghuzhanghao | varchar | 200 | 用户账号 | ||
storeupnum | int | 收藏数量 | |||
sfsh | varchar | 200 | 是否审核 | ||
shhf | longtext | 4294967295 | 回复内容 |
表4-9:酒店预定
字段名称 | 类型 | 长度 | 字段说明 | 主键 | 默认值 |
id | bigint | 主键 | 主键 | ||
addtime | timestamp | 创建时间 | CURRENT_TIMESTAMP | ||
fangjianmingcheng | varchar | 200 | 房间名称 | ||
fangjianleixing | varchar | 200 | 房间类型 | ||
jiudianleixing | varchar | 200 | 酒店类型 | ||
fangjiandizhi | varchar | 200 | 房间地址 | ||
yiwanjiage | double | 一晚价格 | |||
yudingtianshu | int | 预定天数 | |||
zongjia | varchar | 200 | 总价 | ||
fuwudianhua | varchar | 200 | 服务电话 | ||
yudingshijian | datetime | 预定时间 | |||
yonghuzhanghao | varchar | 200 | 用户账号 | ||
yonghuxingming | varchar | 200 | 用户姓名 | ||
ispay | varchar | 200 | 是否支付 | ||
sfsh | varchar | 200 | 是否审核 | ||
shhf | longtext | 4294967295 | 回复内容 | ||
yunyingshangzhanghao | varchar | 200 | 运营商账号 | ||
yunyingshangxingming | varchar | 200 | 运营商姓名 |
表4-10:酒店信息
字段名称 | 类型 | 长度 | 字段说明 | 主键 | 默认值 |
id | bigint | 主键 | 主键 | ||
addtime | timestamp | 创建时间 | CURRENT_TIMESTAMP | ||
fangjianmingcheng | varchar | 200 | 房间名称 | ||
jiudianleixing | varchar | 200 | 酒店类型 | ||
fangjianleixing | varchar | 200 | 房间类型 | ||
fangjiantupian | longtext | 4294967295 | 房间图片 | ||
fangjiandizhi | varchar | 200 | 房间地址 | ||
yiwanjiage | int | 一晚价格 | |||
fuwudianhua | varchar | 200 | 服务电话 | ||
fangneisheshi | longtext | 4294967295 | 房内设施 | ||
storeupnum | int | 收藏数量 | |||
fangjianzhuangtai | varchar | 200 | 房间状态 | ||
yunyingshangzhanghao | varchar | 200 | 运营商账号 | ||
yunyingshangxingming | varchar | 200 | 运营商姓名 | ||
sfsh | varchar | 200 | 是否审核 | ||
shhf | longtext | 4294967295 | 回复内容 |
表4-11:酒店类型
字段名称 | 类型 | 长度 | 字段说明 | 主键 | 默认值 |
id | bigint | 主键 | 主键 | ||
addtime | timestamp | 创建时间 | CURRENT_TIMESTAMP | ||
jiudianleixing | varchar | 200 | 酒店类型 |
表4-12:景点信息
字段名称 | 类型 | 长度 | 字段说明 | 主键 | 默认值 |
id | bigint | 主键 | 主键 | ||
addtime | timestamp | 创建时间 | CURRENT_TIMESTAMP | ||
jingdianmingcheng | varchar | 200 | 景点名称 | ||
jingdiantupian | longtext | 4294967295 | 景点图片 | ||
menpiaojiage | double | 门票价格 | |||
shuliang | int | 数量 | |||
jingdianleixing | varchar | 200 | 景点类型 | ||
kaifangshijian | varchar | 200 | 开放时间 | ||
luxiantuijian | longtext | 4294967295 | 路线推荐 | ||
jingdianjieshao | longtext | 4294967295 | 景点介绍 | ||
jingdiandizhi | varchar | 200 | 景点地址 | ||
storeupnum | int | 收藏数量 | |||
clicktime | datetime | 最近点击时间 | |||
clicknum | int | 点击次数 | |||
yunyingshangzhanghao | varchar | 200 | 运营商账号 | ||
yunyingshangxingming | varchar | 200 | 运营商姓名 | ||
sfsh | varchar | 200 | 是否审核 | ||
shhf | longtext | 4294967295 | 回复内容 |
表4-13:景点类型
字段名称 | 类型 | 长度 | 字段说明 | 主键 | 默认值 |
id | bigint | 主键 | 主键 | ||
addtime | timestamp | 创建时间 | CURRENT_TIMESTAMP | ||
jingdianleixing | varchar | 200 | 景点类型 |
表4-14:攻略类型
字段名称 | 类型 | 长度 | 字段说明 | 主键 | 默认值 |
id | bigint | 主键 | 主键 | ||
addtime | timestamp | 创建时间 | CURRENT_TIMESTAMP | ||
gonglveleixing | varchar | 200 | 攻略类型 |
表4-15:token表
字段名称 | 类型 | 长度 | 字段说明 | 主键 | 默认值 |
id | bigint | 主键 | 主键 | ||
userid | bigint | 用户id | |||
username | varchar | 100 | 用户名 | ||
tablename | varchar | 100 | 表名 | ||
role | varchar | 100 | 角色 | ||
token | varchar | 200 | 密码 | ||
addtime | timestamp | 新增时间 | CURRENT_TIMESTAMP | ||
expiratedtime | timestamp | 过期时间 | CURRENT_TIMESTAMP |
表4-16:运营商
字段名称 | 类型 | 长度 | 字段说明 | 主键 | 默认值 |
id | bigint | 主键 | 主键 | ||
addtime | timestamp | 创建时间 | CURRENT_TIMESTAMP | ||
yunyingshangzhanghao | varchar | 200 | 运营商账号 | ||
mima | varchar | 200 | 密码 | ||
yunyingshangxingming | varchar | 200 | 运营商姓名 | ||
touxiang | longtext | 4294967295 | 头像 | ||
xingbie | varchar | 200 | 性别 | ||
shoujihaoma | varchar | 200 | 手机号码 | ||
sfsh | varchar | 200 | 是否审核 | ||
shhf | longtext | 4294967295 | 回复内容 |
表4-17:酒店信息评论表
字段名称 | 类型 | 长度 | 字段说明 | 主键 | 默认值 |
id | bigint | 主键 | 主键 | ||
addtime | timestamp | 创建时间 | CURRENT_TIMESTAMP | ||
refid | bigint | 关联表id | |||
userid | bigint | 用户id | |||
avatarurl | longtext | 4294967295 | 头像 | ||
nickname | varchar | 200 | 用户名 | ||
content | longtext | 4294967295 | 评论内容 | ||
reply | longtext | 4294967295 | 回复内容 |
5 系统实现
5.1前台用户功能模块
当游客打开系统的网址后,首先看到的就是首页界面。在这里,游客能够看到山西文旅网的导航条显示系统首页、旅游攻略、景点信息、酒店信息、个人中心等,如图5-1所示。
图5-1前台功能界面图
在注册流程中,用户在Vue前端填写必要信息(如用户名、密码等)并提交。前端将这些信息通过HTTP请求发送到Java后端。后端处理这些信息,检查用户名是否唯一,并将新用户数据存入MySQL数据库。完成后,后端向前端发送注册成功的确认,前端随后通知用户完成注册。这个过程实现了新用户的数据收集、验证和存储。如图5-2所示。
图5-2用户注册界面图
在登录流程中,用户首先在Vue前端界面输入用户名和密码。这些信息通过HTTP请求发送到Java后端。后端接收请求,通过与MySQL数据库交互验证用户凭证。如果认证成功,后端会返回给前端,允许用户访问系统。这个过程涵盖了从用户输入到系统验证和响应的全过程。如图5-3所示。
图5-3用户登录界面图
代码
<template>
<div>
<div class="app-contain">
<div class="list_search_view">
<el-form :model="searchQuery" class="search_form" >
<div class="search_view">
<div class="search_label">
运营商账号:
</div>
<div class="search_box">
<el-input class="search_inp" v-model="searchQuery.yunyingshangzhanghao" placeholder="运营商账号"
clearable>
</el-input>
</div>
</div>
<div class="search_view">
<div class="search_label">
审核状态:
</div>
<div class="search_box">
<el-select
class="search_sel"
clearable
v-model="searchQuery.sfsh"
placeholder="审核状态"
>
<el-option v-for="item in approvalLists" :label="item" :value="item"></el-option>
</el-select>
</div>
</div>
<div class="search_btn_view">
<el-button class="search_btn" type="primary" @click="searchClick()" size="small">搜索</el-button>
</div>
</el-form>
<br>
<div class="btn_view">
<el-button type="success" @click="addClick" v-if="btnAuth('yunyingshang','新增')">新增</el-button>
<el-button v-if=" btnAuth('yunyingshang','查看')" type="info" :disabled="selRows.length==1?false:true" @click="infoClick(null)">详情</el-button>
<el-button type="primary" :disabled="selRows.length==1?false:true" @click="editClick" v-if=" btnAuth('yunyingshang','修改')">修改</el-button>
<el-button type="danger" :disabled="selRows.length?false:true" @click="delClick(null)" v-if="btnAuth('yunyingshang','删除')">删除</el-button>
</div>
</div>
<br>
<el-table
v-loading="listLoading"
border
:stripe='true'
@selection-change="handleSelectionChange"
ref="table"
v-if="btnAuth('yunyingshang','查看')"
:data="list"
@row-click="listChange">
<el-table-column :resizable='true' align="left" header-align="left" type="selection" width="55" />
<el-table-column label="序号" width="70" :resizable='true' :sortable='true' align="left" header-align="left">
<template #default="scope">{{ scope.$index + 1}}</template>
</el-table-column>
<el-table-column
:resizable='true'
:sortable='true'
align="left"
header-align="left"
label="运营商账号">
<template #default="scope">
{{scope.row.yunyingshangzhanghao}}
</template>
</el-table-column>
<el-table-column
:resizable='true'
:sortable='true'
align="left"
header-align="left"
label="运营商姓名">
<template #default="scope">
{{scope.row.yunyingshangxingming}}
</template>
</el-table-column>
<el-table-column label="头像" width="120" :resizable='true' :sortable='true' align="left" header-align="left">
<template #default="scope">
<div v-if="scope.row.touxiang">
<el-image v-if="scope.row.touxiang.substring(0,4)=='http'" preview-teleported
:preview-src-list="[scope.row.touxiang.split(',')[0]]"
:src="scope.row.touxiang.split(',')[0]" style="width:100px;height:100px"></el-image>
<el-image v-else preview-teleported
:preview-src-list="[$config.url+scope.row.touxiang.split(',')[0]]"
:src="$config.url+scope.row.touxiang.split(',')[0]" style="width:100px;height:100px">
</el-image>
</div>
<div v-else>无图片</div>
</template>
</el-table-column>
<el-table-column
:resizable='true'
:sortable='true'
align="left"
header-align="left"
label="性别">
<template #default="scope">
{{scope.row.xingbie}}
</template>
</el-table-column>
<el-table-column
:resizable='true'
:sortable='true'
align="left"
header-align="left"
label="手机号码">
<template #default="scope">
{{scope.row.shoujihaoma}}
</template>
</el-table-column>
<el-table-column label="审核回复" :resizable='true' :sortable='true' align="left" header-align="left">
<template #default="scope">
{{scope.row.shhf}}
</template>
</el-table-column>
<el-table-column label="审核状态" :resizable='true' :sortable='true' align="left" header-align="left">
<template #default="scope">
<el-tag type="success" v-if="scope.row.sfsh=='是'">通过</el-tag>
<el-tag type="danger" v-else-if="scope.row.sfsh=='否'">未通过</el-tag>
<el-tag type="warning" v-else>待审核</el-tag>
</template>
</el-table-column>
<el-table-column label="审核" v-if="btnAuth('yunyingshang','审核')" :resizable='true' :sortable='true' align="left" header-align="left">
<template #default="scope">
<el-button type="text" @click="approvalClick(scope.row)">审核</el-button>
</template>
</el-table-column>
<el-table-column label="操作" width="300" :resizable='true' :sortable='true' align="left" header-align="left">
<template #default="scope">
<el-button type="info" v-if=" btnAuth('yunyingshang','查看')" @click="infoClick(scope.row.id)">详情</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
background
:layout="layouts.join(',')"
:total="total"
:page-size="listQuery.limit"
prev-text="Prev"
next-text="Next"
:hide-on-single-page="false"
:style='{"padding":"0","margin":"20px 0 0","whiteSpace":"nowrap","color":"#333","textAlign":"center","width":"100%","fontWeight":"500"}'
@size-change="sizeChange"
@current-change="currentChange"
@prev-click="prevClick"
@next-click="nextClick" />
</div>
<formModel ref="formRef" @formModelChange="formModelChange"></formModel>
<Approval ref="approvalRef" :tableName="tableName" @shChange="searchClick()"></Approval>
</div>
</template>
<script setup>
import axios from 'axios'
import {
reactive,
ref,
getCurrentInstance,
nextTick,
onMounted,
watch,
} from 'vue'
import {
useRoute,
useRouter
} from 'vue-router'
import {
ElMessageBox
} from 'element-plus'
const context = getCurrentInstance()?.appContext.config.globalProperties;
import formModel from './formModel.vue'
//基础信息
const tableName = 'yunyingshang'
const formName = '运营商'
const route = useRoute()
//基础信息
onMounted(()=>{
})
//列表数据
const list = ref(null)
const table = ref(null)
const listQuery = ref({
page: 1,
limit: 20,
sort: 'id',
order: 'desc'
})
const searchQuery = ref({})
const selRows = ref([])
const listLoading = ref(false)
const listChange = (row) =>{
nextTick(()=>{
table.value.clearSelection()
table.value.toggleRowSelection(row)
})
}
//列表
const getList = () => {
listLoading.value = true
let params = JSON.parse(JSON.stringify(listQuery.value))
params['sort'] = 'id'
params['order'] = 'desc'
if(searchQuery.value.yunyingshangzhanghao&&searchQuery.value.yunyingshangzhanghao!=''){
params['yunyingshangzhanghao'] = '%' + searchQuery.value.yunyingshangzhanghao + '%'
}
if(searchQuery.value.sfsh && searchQuery.value.sfsh!=''){
params['sfsh'] = searchQuery.value.sfsh
}
if(searchQuery.value.sfsh && searchQuery.value.sfsh!=''){
params['sfsh'] = searchQuery.value.sfsh
}
if(searchQuery.value.sfsh && searchQuery.value.sfsh!=''){
params['sfsh'] = searchQuery.value.sfsh
}
if(searchQuery.value.sfsh && searchQuery.value.sfsh!=''){
params['sfsh'] = searchQuery.value.sfsh
}
if(searchQuery.value.sfsh && searchQuery.value.sfsh!=''){
params['sfsh'] = searchQuery.value.sfsh
}
if(searchQuery.value.sfsh && searchQuery.value.sfsh!=''){
params['sfsh'] = searchQuery.value.sfsh
}
if(searchQuery.value.sfsh && searchQuery.value.sfsh!=''){
params['sfsh'] = searchQuery.value.sfsh
}
if(searchQuery.value.sfsh && searchQuery.value.sfsh!=''){
params['sfsh'] = searchQuery.value.sfsh
}
context?.$http({
url: `${tableName}/page`,
method: 'get',
params: params
}).then(res => {
listLoading.value = false
list.value = res.data.data.list
total.value = Number(res.data.data.total)
})
}
//删
const delClick = (id) => {
let ids = ref([])
if (id) {
ids.value = [id]
} else {
if (selRows.value.length) {
for (let x in selRows.value) {
ids.value.push(selRows.value[x].id)
}
} else {
return false
}
}
ElMessageBox.confirm(`是否删除选中${formName}`, '提示', {
confirmButtonText: '是',
cancelButtonText: '否',
type: 'warning',
}).then(() => {
context?.$http({
url: `${tableName}/delete`,
method: 'post',
data: ids.value
}).then(res => {
context?.$toolUtil.message('删除成功', 'success',()=>{
getList()
})
})
})
}
//多选
const handleSelectionChange = (e) => {
selRows.value = e
}
//列表数据
//分页
const total = ref(0)
const layouts = ref(["prev","pager","next"])
const sizeChange = (size) => {
listQuery.value.limit = size
getList()
}
const currentChange = (page) => {
listQuery.value.page = page
getList()
}
const prevClick = () => {
listQuery.value.page = listQuery.value.page - 1
getList()
}
const nextClick = () => {
listQuery.value.page = listQuery.value.page + 1
getList()
}
//分页
//权限验证
const btnAuth = (e,a)=>{
return context?.$toolUtil.isAuth(e,a)
}
//搜索
const searchClick = () => {
listQuery.value.page = 1
getList()
}
//表单
const formRef = ref(null)
const formModelChange=()=>{
searchClick()
}
const addClick = ()=>{
formRef.value.init()
}
const editClick = ()=>{
if(selRows.value.length){
formRef.value.init(selRows.value[0].id,'edit')
}
}
const infoClick = (id=null)=>{
if(id){
formRef.value.init(id,'info')
}
else if(selRows.value.length){
formRef.value.init(selRows.value[0].id,'info')
}
}
// 表单
// 预览文件
const preClick = (file) =>{
if(!file){
context?.$toolUtil.message('文件不存在','error')
}
window.open(context?.$config.url + file)
// const a = document.createElement('a');
// a.style.display = 'none';
// a.setAttribute('target', '_blank');
// file && a.setAttribute('download', file);
// a.href = context?.$config.url + file;
// document.body.appendChild(a);
// a.click();
// document.body.removeChild(a);
}
// 下载文件
const download = (file) => {
if(!file){
context?.$toolUtil.message('文件不存在','error')
}
let arr = file.replace(new RegExp('file/', "g"), "")
axios.get((location.href.split(context?.$config.name).length>1 ? location.href.split(context?.$config.name)[0] :'') + context?.$config.name + '/file/download?fileName=' + arr, {
headers: {
token: context?.$toolUtil.storageGet('Token')
},
responseType: "blob"
}).then(({
data
}) => {
const binaryData = [];
binaryData.push(data);
const objectUrl = window.URL.createObjectURL(new Blob(binaryData, {
type: 'application/pdf;chartset=UTF-8'
}))
const a = document.createElement('a')
a.href = objectUrl
a.download = arr
// a.click()
// 下面这个写法兼容火狐
a.dispatchEvent(new MouseEvent('click', {
bubbles: true,
cancelable: true,
view: window
}))
window.URL.revokeObjectURL(data)
})
}
//审核
import Approval from '@/components/common/approval.vue'
const approvalRef = ref(null)
const approvalClick = (row) => {
let params = {
id:row.id,
yunyingshangzhanghao: row.yunyingshangzhanghao,
mima: row.mima,
yunyingshangxingming: row.yunyingshangxingming,
touxiang: row.touxiang,
xingbie: row.xingbie,
shoujihaoma: row.shoujihaoma,
sfsh: row.sfsh,
shhf: row.shhf,
}
nextTick(() => {
approvalRef.value.approvalClick(params )
})
}
//查询审核状态列表
const approvalLists = ref([])
//初始化
const init = () => {
approvalLists.value = "是,否,待审核".split(',');
getList()
}
init()
</script>
<style lang="scss" scoped>
// 操作盒子
.list_search_view {
margin: 0 0 20px;
display: flex;
justify-content: space-between;
flex-wrap: wrap;
// 搜索盒子
.search_form {
display: flex;
align-items: center;
order: 2;
// 子盒子
.search_view {
margin: 0 10px 0 0;
display: flex;
align-items: center;
// 搜索label
.search_label {
margin: 0 10px 0 0;
color: #fff;
background: none;
font-weight: 500;
display: inline-block;
width: auto;
font-size: 14px;
line-height: 40px;
text-align: right;
min-width: 100px;
height: 40px;
}
// 搜索item
.search_box {
display: inline-block;
width: auto;
// 输入框
:deep(.search_inp) {
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 4px;
padding: 0 10px;
color: #fff;
background: rgba(0, 0, 0, 0.1);
width: auto;
line-height: 34px;
box-sizing: border-box;
//去掉默认样式
.el-input__wrapper{
border: none;
box-shadow: none;
background: none;
border-radius: 0;
height: 100%;
padding: 0;
}
.is-focus {
box-shadow: none !important;
}
}
// 下拉框
:deep(.search_sel) {
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 4px;
padding: 0 10px;
color: #fff;
background: rgba(0, 0, 0, 0.1);
width: auto;
line-height: 34px;
box-sizing: border-box;
//去掉默认样式
.select-trigger{
height: 100%;
.el-input{
height: 100%;
.el-input__wrapper{
border: none;
box-shadow: none;
background: none;
border-radius: 0;
height: 100%;
padding: 0;
}
.is-focus {
box-shadow: none !important;
}
}
}
}
}
}
// 搜索按钮盒子
.search_btn_view {
width: 20%;
display: flex;
padding: 0 20px;
// 搜索按钮
.search_btn {
border: 1px solid #357ebd;
cursor: pointer;
border-radius: 4px;
padding: 0 24px;
color: #fff;
background: rgba(66, 139, 202, 0.45);
width: auto;
font-size: 14px;
height: 36px;
}
// 搜索按钮-悬浮
.search_btn:hover {
border: 1px solid #357ebd;
background: rgba(66, 139, 202, 0.45);
}
}
}
//头部按钮盒子
.btn_view {
margin: 0;
display: flex;
// 其他
:deep(.el-button--default){
border: 1px solid #ccc;
cursor: pointer;
border-radius: 3px;
padding: 0 24px;
margin: 0 10px 0 0;
outline: none;
color: #fff;
background: rgba(222, 222, 222, 0.55);
width: auto;
font-size: 14px;
height: 36px;
}
// 其他-悬浮
:deep(.el-button--default:hover){
background: rgba(222, 222, 222, 0.45);
}
// 新增
:deep(.el-button--success){
border: 1px solid #357ebd;
cursor: pointer;
border-radius: 3px;
padding: 0 24px;
margin: 0 10px 0 0;
outline: none;
color: #fff;
background: rgba(66, 139, 202, 0.45);
width: auto;
font-size: 14px;
height: 36px;
}
// 新增-悬浮
:deep(.el-button--success:hover){
background: rgba(66, 139, 202, 0.35);
}
// 修改
:deep(.el-button--primary){
border: 1px solid #4cae4c;
cursor: pointer;
border-radius: 3px;
padding: 0 24px;
margin: 0 10px 0 0;
outline: none;
color: #fff;
background: rgba(92, 184, 92, 0.55);
width: auto;
font-size: 14px;
height: 36px;
}
// 修改-悬浮
:deep(.el-button--primary:hover){
background: rgba(92, 184, 92, 0.45);
}
// 详情
:deep(.el-button--info){
border: 1px solid #46b8da;
cursor: pointer;
border-radius: 3px;
padding: 0 24px;
margin: 0 10px 0 0;
outline: none;
color: #fff;
background: rgba(91, 192, 222, 0.45);
width: auto;
font-size: 14px;
height: 36px;
}
// 详情-悬浮
:deep(.el-button--info:hover){
background: rgba(91, 192, 222, 0.35);
}
// 删除
:deep(.el-button--danger){
border: 1px solid #d43f3a;
cursor: pointer;
border-radius: 3px;
padding: 0 24px;
margin: 0 10px 0 0;
outline: none;
color: #fff;
background: rgba(217, 83, 79, 0.55);
width: auto;
font-size: 14px;
height: 36px;
}
// 删除-悬浮
:deep(.el-button--danger:hover){
background: rgba(217, 83, 79, 0.45);
}
// 统计
:deep(.el-button--warning){
border: 1px solid #eea236;
cursor: pointer;
border-radius: 3px;
padding: 0 24px;
margin: 0 10px 0 0;
outline: none;
color: #fff;
background: rgba(240, 173, 78, 0.55);
width: auto;
font-size: 14px;
height: 36px;
}
// 统计-悬浮
:deep(.el-button--warning:hover){
background: rgba(240, 173, 78, 0.45);
}
}
}
// 表格样式
.el-table {
border-radius: 0px;
padding: 0;
background: rgba(0, 0, 0, 0.25);
width: 100%;
border-color: rgba(254, 182, 203, 0.5);
border-width: 1px 0 0 1px;
border-style: solid;
:deep(.el-table__header-wrapper) {
thead {
color: #fff;
font-weight: 500;
width: 100%;
tr {
background: rgba(0, 0, 0, 0.1);
th {
padding: 4px 0;
background: none;
border-color: rgba(254, 182, 203, 0.5);
border-width: 0 0px 1px 0;
border-style: solid;
text-align: left;
.cell {
padding: 0 10px;
word-wrap: normal;
word-break: break-all;
white-space: normal;
font-weight: bold;
display: inline-block;
vertical-align: middle;
width: 100%;
line-height: 24px;
position: relative;
text-overflow: ellipsis;
}
}
}
}
}
:deep(.el-table__body-wrapper) {
tbody {
width: 100%;
tr {
background: none;
td {
padding: 6px 0;
color: #eee;
background: none;
border-color: #d2d2d2;
border-width: 0 0px 0px 0;
border-style: solid;
text-align: left;
.cell {
padding: 0 10px;
overflow: hidden;
word-break: break-all;
white-space: normal;
line-height: 24px;
text-overflow: ellipsis;
// 编辑
.el-button--primary {
border: 1px solid #d58512;
cursor: pointer;
border-radius: 4px;
padding: 5px 10px;
margin: 0 6px 6px 0;
color: #fff;
background: rgba(240, 173, 78, 0.45);
width: auto;
font-size: 12px;
height: auto;
}
// 编辑-悬浮
.el-button--primary:hover {
}
// 详情
.el-button--info {
border: 1px solid #357ebd;
cursor: pointer;
border-radius: 3px;
padding: 5px 10px;
margin: 0 6px 6px 0;
color: #fff;
background: rgba(66, 139, 202, 0.55);
width: auto;
font-size: 12px;
height: auto;
}
// 详情-悬浮
.el-button--info:hover {
}
// 删除
.el-button--danger {
border: 1px solid #ac2925;
cursor: pointer;
border-radius: 3px;
padding: 5px 10px;
margin: 0 6px 6px 0;
color: #fff;
background: rgba(217, 83, 79, 0.45);
width: auto;
font-size: 12px;
height: auto;
}
// 删除-悬浮
.el-button--danger:hover {
}
// 跨表
.el-button--success {
border: 1px solid #ccc;
cursor: pointer;
border-radius: 3px;
padding: 5px 10px;
margin: 0 6px 6px 0;
color: #fff;
background: rgba(222, 222, 222, 0.55);
width: auto;
font-size: 12px;
height: 24px;
}
// 跨表-悬浮
.el-button--success:hover {
}
// 操作
.el-button--warning {
border: 1px solid #4cae4c;
cursor: pointer;
border-radius: 3px;
padding: 5px 10px;
margin: 0 6px 6px 0;
color: #fff;
background: rgba(92, 184, 92, 0.55);
width: auto;
font-size: 12px;
height: auto;
}
// 操作-悬浮
.el-button--warning:hover {
}
}
}
}
tr.el-table__row--striped {
td {
background: rgba(0, 0, 0, 0.10);
}
}
tr:hover {
td {
padding: 6px 0;
color: #fff;
background: rgba(0, 0, 0, 0.10);
border-color: #d2d2d2;
border-width: 0 0px 0px 0;
border-style: solid;
text-align: left;
}
}
}
}
}
// 分页器
.el-pagination {
// 总页码
:deep(.el-pagination__total) {
margin: 0 10px 0 0;
color: #666;
font-weight: 400;
display: inline-block;
vertical-align: top;
font-size: 13px;
line-height: 28px;
height: 28px;
}
// 上一页
:deep(.btn-prev) {
border: none;
border-radius: 0px;
padding: 0 5px;
margin: 0 5px;
color: #fff;
background: none;
display: inline-block;
vertical-align: top;
font-size: 13px;
line-height: 26px;
min-width: 35px;
height: 26px;
}
// 下一页
:deep(.btn-next) {
border: none;
border-radius: 0px;
padding: 0 5px;
margin: 0 5px;
color: #fff;
background: none;
display: inline-block;
vertical-align: top;
font-size: 13px;
line-height: 26px;
min-width: 35px;
height: 26px;
}
// 上一页禁用
:deep(.btn-prev:disabled) {
border: none;
cursor: not-allowed;
border-radius: 0px;
padding: 0 5px;
margin: 0 5px;
color: #fff;
background: none;
display: inline-block;
vertical-align: top;
font-size: 13px;
line-height: 26px;
height: 26px;
}
// 下一页禁用
:deep(.btn-next:disabled) {
border: none;
cursor: not-allowed;
border-radius: 0px;
padding: 0 5px;
margin: 0 5px;
color: #fff;
background: none;
display: inline-block;
vertical-align: top;
font-size: 13px;
line-height: 26px;
height: 26px;
}
// 页码
:deep(.el-pager) {
padding: 0;
margin: 0;
display: inline-block;
vertical-align: top;
// 数字
.number {
cursor: pointer;
border: 1px solid rgba(255, 255, 255, 0.3);
padding: 0 4px;
margin: 0 5px;
color: #fff;
display: inline-block;
vertical-align: top;
font-size: 13px;
line-height: 26px;
border-radius: 0px;
background: none;
text-align: center;
min-width: 30px;
height: 26px;
}
// 数字悬浮
.number:hover {
cursor: pointer;
border: 1px solid rgba(0, 0, 0, 0.05);
padding: 0 4px;
margin: 0 5px;
color: #fff;
display: inline-block;
vertical-align: top;
font-size: 13px;
line-height: 26px;
border-radius: 0px;
background: rgba(0, 0, 0, 0.25);
text-align: center;
min-width: 30px;
height: 26px;
}
// 选中
.number.is-active {
cursor: default;
border: 1px solid rgba(0, 0, 0, 0.05);
padding: 0 4px;
margin: 0 5px;
color: #fff;
display: inline-block;
vertical-align: top;
font-size: 13px;
line-height: 26px;
border-radius: 0px;
background: rgba(0, 0, 0, 0.25);
text-align: center;
min-width: 30px;
height: 26px;
}
}
// sizes
:deep(.el-pagination__sizes) {
display: inline-block;
vertical-align: top;
font-size: 13px;
line-height: 28px;
height: 28px;
.el-select {
border: 1px solid #DCDFE6;
cursor: pointer;
padding: 0;
color: #606266;
display: inline-block;
font-size: 13px;
line-height: 28px;
border-radius: 3px;
outline: 0;
background: #FFF;
width: 100%;
text-align: center;
height: 28px;
}
}
// 跳页
:deep(.el-pagination__jump) {
margin: 0 0 0 24px;
color: #606266;
display: inline-block;
vertical-align: top;
font-size: 13px;
line-height: 28px;
height: 28px;
// 输入框
.el-input {
border: 1px solid #DCDFE6;
cursor: pointer;
padding: 0 3px;
color: #606266;
display: inline-block;
font-size: 14px;
line-height: 28px;
border-radius: 3px;
outline: 0;
background: #FFF;
width: 100%;
text-align: center;
height: 28px;
//去掉默认样式
.el-input__wrapper{
border: none;
box-shadow: none;
background: none;
border-radius: 0;
height: 100%;
padding: 0;
}
.is-focus {
box-shadow: none !important;
}
}
}
}
</style>
用户点击旅游攻略,在旅游攻略页面的搜索栏输入攻略名称、起点,进行搜索,然后可以查看目的地、出行方式、美食推荐、景点推荐、酒店推荐、游玩天数、预计费用、用户账号、收藏数量等信息,如有需要可以进行收藏等操作;如图5-4所示。
图5-4旅游攻略界面图
用户点击景点信息,在景点信息页面的搜索栏输入景点名称,进行搜索,然后可以查看景点图片、门票价格、数量、景点类型、开放时间、景点地址、运营商账号、运营商姓名等信息,如有需要可以点击购买、收藏等操作;如图5-5所示。
图5-5景点信息界面图
用户点击个人中心,在个人中心页面可以修改个人信息、密码修改进行详细操作,还可以对修改密码、酒店信息管理、旅游路线管理、我的收藏管理、景点信息管理进行详细操作;如图5-6所示。
图5-6个人中心界面图
5.2 后台管理员功能模块
管理员登录,通过登录页面输入用户名、密码、角色,进行登录操作,如图5-7所示。
图5-7管理员登录界面图
管理员登录进入山西文旅网可以查看首页、酒店信息管理、景点信息管理、旅游路线管理、管理员管理、运营商管理、用户管理等信息,进行相应操作,如图5-8所示。
图5-8管理员功能界面图
酒店信息功能在视图层(view层)进行交互,比如点击“新增”按钮或填写酒店信息表单。这些酒店信息动作被视图层捕获并作为请求发送给相应的控制器层(control1er层)。控制器接收到这些请求后,调用服务层(service层)以执行相关的业务逻辑,例如验证输入数据的有效性和与数据库的交互。服务层处理完这些逻辑后,进一步与数据访问对象层(DAO层)交互,后者负责具体的数据操作如搜索、新增、更新或删除酒店信息,并将操作结果返回给控制器。最终,控制器根据这些结果更新视图层,以便酒店信息功能可以看到最新的信息或相应的操作反馈。在酒店信息页面的输入栏中输入房间名称、房间类型、审核状态进行搜索,可以查看到酒店详细信息,并根据需要进行修改或者删除等操作;如图5-9所示。
图5-9酒店信息界面图