11 changed files with 469 additions and 864 deletions
@ -0,0 +1,104 @@ |
|||||
|
package com.ruoyi.web.controller; |
||||
|
|
||||
|
import java.util.List; |
||||
|
import javax.servlet.http.HttpServletResponse; |
||||
|
import org.springframework.security.access.prepost.PreAuthorize; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.web.bind.annotation.GetMapping; |
||||
|
import org.springframework.web.bind.annotation.PostMapping; |
||||
|
import org.springframework.web.bind.annotation.PutMapping; |
||||
|
import org.springframework.web.bind.annotation.DeleteMapping; |
||||
|
import org.springframework.web.bind.annotation.PathVariable; |
||||
|
import org.springframework.web.bind.annotation.RequestBody; |
||||
|
import org.springframework.web.bind.annotation.RequestMapping; |
||||
|
import org.springframework.web.bind.annotation.RestController; |
||||
|
import com.ruoyi.common.annotation.Log; |
||||
|
import com.ruoyi.common.core.controller.BaseController; |
||||
|
import com.ruoyi.common.core.domain.AjaxResult; |
||||
|
import com.ruoyi.common.enums.BusinessType; |
||||
|
import com.ruoyi.system.domain.Users; |
||||
|
import com.ruoyi.system.service.IUsersService; |
||||
|
import com.ruoyi.common.utils.poi.ExcelUtil; |
||||
|
import com.ruoyi.common.core.page.TableDataInfo; |
||||
|
|
||||
|
/** |
||||
|
* 系统用户Controller |
||||
|
* |
||||
|
* @author ruoyi |
||||
|
* @date 2026-01-14 |
||||
|
*/ |
||||
|
@RestController |
||||
|
@RequestMapping("/system/users") |
||||
|
public class UsersController extends BaseController |
||||
|
{ |
||||
|
@Autowired |
||||
|
private IUsersService usersService; |
||||
|
|
||||
|
/** |
||||
|
* 查询系统用户列表 |
||||
|
*/ |
||||
|
@PreAuthorize("@ss.hasPermi('system:users:list')") |
||||
|
@GetMapping("/list") |
||||
|
public TableDataInfo list(Users users) |
||||
|
{ |
||||
|
startPage(); |
||||
|
List<Users> list = usersService.selectUsersList(users); |
||||
|
return getDataTable(list); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 导出系统用户列表 |
||||
|
*/ |
||||
|
@PreAuthorize("@ss.hasPermi('system:users:export')") |
||||
|
@Log(title = "系统用户", businessType = BusinessType.EXPORT) |
||||
|
@PostMapping("/export") |
||||
|
public void export(HttpServletResponse response, Users users) |
||||
|
{ |
||||
|
List<Users> list = usersService.selectUsersList(users); |
||||
|
ExcelUtil<Users> util = new ExcelUtil<Users>(Users.class); |
||||
|
util.exportExcel(response, list, "系统用户数据"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取系统用户详细信息 |
||||
|
*/ |
||||
|
@PreAuthorize("@ss.hasPermi('system:users:query')") |
||||
|
@GetMapping(value = "/{id}") |
||||
|
public AjaxResult getInfo(@PathVariable("id") Long id) |
||||
|
{ |
||||
|
return success(usersService.selectUsersById(id)); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 新增系统用户 |
||||
|
*/ |
||||
|
@PreAuthorize("@ss.hasPermi('system:users:add')") |
||||
|
@Log(title = "系统用户", businessType = BusinessType.INSERT) |
||||
|
@PostMapping |
||||
|
public AjaxResult add(@RequestBody Users users) |
||||
|
{ |
||||
|
return toAjax(usersService.insertUsers(users)); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 修改系统用户 |
||||
|
*/ |
||||
|
@PreAuthorize("@ss.hasPermi('system:users:edit')") |
||||
|
@Log(title = "系统用户", businessType = BusinessType.UPDATE) |
||||
|
@PutMapping |
||||
|
public AjaxResult edit(@RequestBody Users users) |
||||
|
{ |
||||
|
return toAjax(usersService.updateUsers(users)); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 删除系统用户 |
||||
|
*/ |
||||
|
@PreAuthorize("@ss.hasPermi('system:users:remove')") |
||||
|
@Log(title = "系统用户", businessType = BusinessType.DELETE) |
||||
|
@DeleteMapping("/{ids}") |
||||
|
public AjaxResult remove(@PathVariable Long[] ids) |
||||
|
{ |
||||
|
return toAjax(usersService.deleteUsersByIds(ids)); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,82 @@ |
|||||
|
package com.ruoyi.system.domain; |
||||
|
|
||||
|
import org.apache.commons.lang3.builder.ToStringBuilder; |
||||
|
import org.apache.commons.lang3.builder.ToStringStyle; |
||||
|
import com.ruoyi.common.annotation.Excel; |
||||
|
import com.ruoyi.common.core.domain.BaseEntity; |
||||
|
|
||||
|
/** |
||||
|
* 系统用户对象 users |
||||
|
* |
||||
|
* @author ruoyi |
||||
|
* @date 2026-01-14 |
||||
|
*/ |
||||
|
public class Users extends BaseEntity |
||||
|
{ |
||||
|
private static final long serialVersionUID = 1L; |
||||
|
|
||||
|
/** 主键ID */ |
||||
|
private Long id; |
||||
|
|
||||
|
/** 登录账号 */ |
||||
|
@Excel(name = "登录账号") |
||||
|
private String username; |
||||
|
|
||||
|
/** 密码 (建议存哈希值) */ |
||||
|
@Excel(name = "密码 (建议存哈希值)") |
||||
|
private String password; |
||||
|
|
||||
|
/** 权限等级: 1=管理员(L1), 2=房主(L2), 3=操作员(L3) */ |
||||
|
@Excel(name = "权限等级: 1=管理员(L1), 2=房主(L2), 3=操作员(L3)") |
||||
|
private Long role; |
||||
|
|
||||
|
public void setId(Long id) |
||||
|
{ |
||||
|
this.id = id; |
||||
|
} |
||||
|
|
||||
|
public Long getId() |
||||
|
{ |
||||
|
return id; |
||||
|
} |
||||
|
|
||||
|
public void setUsername(String username) |
||||
|
{ |
||||
|
this.username = username; |
||||
|
} |
||||
|
|
||||
|
public String getUsername() |
||||
|
{ |
||||
|
return username; |
||||
|
} |
||||
|
|
||||
|
public void setPassword(String password) |
||||
|
{ |
||||
|
this.password = password; |
||||
|
} |
||||
|
|
||||
|
public String getPassword() |
||||
|
{ |
||||
|
return password; |
||||
|
} |
||||
|
|
||||
|
public void setRole(Long role) |
||||
|
{ |
||||
|
this.role = role; |
||||
|
} |
||||
|
|
||||
|
public Long getRole() |
||||
|
{ |
||||
|
return role; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public String toString() { |
||||
|
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) |
||||
|
.append("id", getId()) |
||||
|
.append("username", getUsername()) |
||||
|
.append("password", getPassword()) |
||||
|
.append("role", getRole()) |
||||
|
.toString(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,61 @@ |
|||||
|
package com.ruoyi.system.mapper; |
||||
|
|
||||
|
import java.util.List; |
||||
|
import com.ruoyi.system.domain.Users; |
||||
|
|
||||
|
/** |
||||
|
* 系统用户Mapper接口 |
||||
|
* |
||||
|
* @author ruoyi |
||||
|
* @date 2026-01-14 |
||||
|
*/ |
||||
|
public interface UsersMapper |
||||
|
{ |
||||
|
/** |
||||
|
* 查询系统用户 |
||||
|
* |
||||
|
* @param id 系统用户主键 |
||||
|
* @return 系统用户 |
||||
|
*/ |
||||
|
public Users selectUsersById(Long id); |
||||
|
|
||||
|
/** |
||||
|
* 查询系统用户列表 |
||||
|
* |
||||
|
* @param users 系统用户 |
||||
|
* @return 系统用户集合 |
||||
|
*/ |
||||
|
public List<Users> selectUsersList(Users users); |
||||
|
|
||||
|
/** |
||||
|
* 新增系统用户 |
||||
|
* |
||||
|
* @param users 系统用户 |
||||
|
* @return 结果 |
||||
|
*/ |
||||
|
public int insertUsers(Users users); |
||||
|
|
||||
|
/** |
||||
|
* 修改系统用户 |
||||
|
* |
||||
|
* @param users 系统用户 |
||||
|
* @return 结果 |
||||
|
*/ |
||||
|
public int updateUsers(Users users); |
||||
|
|
||||
|
/** |
||||
|
* 删除系统用户 |
||||
|
* |
||||
|
* @param id 系统用户主键 |
||||
|
* @return 结果 |
||||
|
*/ |
||||
|
public int deleteUsersById(Long id); |
||||
|
|
||||
|
/** |
||||
|
* 批量删除系统用户 |
||||
|
* |
||||
|
* @param ids 需要删除的数据主键集合 |
||||
|
* @return 结果 |
||||
|
*/ |
||||
|
public int deleteUsersByIds(Long[] ids); |
||||
|
} |
||||
@ -0,0 +1,61 @@ |
|||||
|
package com.ruoyi.system.service; |
||||
|
|
||||
|
import java.util.List; |
||||
|
import com.ruoyi.system.domain.Users; |
||||
|
|
||||
|
/** |
||||
|
* 系统用户Service接口 |
||||
|
* |
||||
|
* @author ruoyi |
||||
|
* @date 2026-01-14 |
||||
|
*/ |
||||
|
public interface IUsersService |
||||
|
{ |
||||
|
/** |
||||
|
* 查询系统用户 |
||||
|
* |
||||
|
* @param id 系统用户主键 |
||||
|
* @return 系统用户 |
||||
|
*/ |
||||
|
public Users selectUsersById(Long id); |
||||
|
|
||||
|
/** |
||||
|
* 查询系统用户列表 |
||||
|
* |
||||
|
* @param users 系统用户 |
||||
|
* @return 系统用户集合 |
||||
|
*/ |
||||
|
public List<Users> selectUsersList(Users users); |
||||
|
|
||||
|
/** |
||||
|
* 新增系统用户 |
||||
|
* |
||||
|
* @param users 系统用户 |
||||
|
* @return 结果 |
||||
|
*/ |
||||
|
public int insertUsers(Users users); |
||||
|
|
||||
|
/** |
||||
|
* 修改系统用户 |
||||
|
* |
||||
|
* @param users 系统用户 |
||||
|
* @return 结果 |
||||
|
*/ |
||||
|
public int updateUsers(Users users); |
||||
|
|
||||
|
/** |
||||
|
* 批量删除系统用户 |
||||
|
* |
||||
|
* @param ids 需要删除的系统用户主键集合 |
||||
|
* @return 结果 |
||||
|
*/ |
||||
|
public int deleteUsersByIds(Long[] ids); |
||||
|
|
||||
|
/** |
||||
|
* 删除系统用户信息 |
||||
|
* |
||||
|
* @param id 系统用户主键 |
||||
|
* @return 结果 |
||||
|
*/ |
||||
|
public int deleteUsersById(Long id); |
||||
|
} |
||||
@ -0,0 +1,93 @@ |
|||||
|
package com.ruoyi.system.service.impl; |
||||
|
|
||||
|
import java.util.List; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import com.ruoyi.system.mapper.UsersMapper; |
||||
|
import com.ruoyi.system.domain.Users; |
||||
|
import com.ruoyi.system.service.IUsersService; |
||||
|
|
||||
|
/** |
||||
|
* 系统用户Service业务层处理 |
||||
|
* |
||||
|
* @author ruoyi |
||||
|
* @date 2026-01-14 |
||||
|
*/ |
||||
|
@Service |
||||
|
public class UsersServiceImpl implements IUsersService |
||||
|
{ |
||||
|
@Autowired |
||||
|
private UsersMapper usersMapper; |
||||
|
|
||||
|
/** |
||||
|
* 查询系统用户 |
||||
|
* |
||||
|
* @param id 系统用户主键 |
||||
|
* @return 系统用户 |
||||
|
*/ |
||||
|
@Override |
||||
|
public Users selectUsersById(Long id) |
||||
|
{ |
||||
|
return usersMapper.selectUsersById(id); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 查询系统用户列表 |
||||
|
* |
||||
|
* @param users 系统用户 |
||||
|
* @return 系统用户 |
||||
|
*/ |
||||
|
@Override |
||||
|
public List<Users> selectUsersList(Users users) |
||||
|
{ |
||||
|
return usersMapper.selectUsersList(users); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 新增系统用户 |
||||
|
* |
||||
|
* @param users 系统用户 |
||||
|
* @return 结果 |
||||
|
*/ |
||||
|
@Override |
||||
|
public int insertUsers(Users users) |
||||
|
{ |
||||
|
return usersMapper.insertUsers(users); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 修改系统用户 |
||||
|
* |
||||
|
* @param users 系统用户 |
||||
|
* @return 结果 |
||||
|
*/ |
||||
|
@Override |
||||
|
public int updateUsers(Users users) |
||||
|
{ |
||||
|
return usersMapper.updateUsers(users); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 批量删除系统用户 |
||||
|
* |
||||
|
* @param ids 需要删除的系统用户主键 |
||||
|
* @return 结果 |
||||
|
*/ |
||||
|
@Override |
||||
|
public int deleteUsersByIds(Long[] ids) |
||||
|
{ |
||||
|
return usersMapper.deleteUsersByIds(ids); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 删除系统用户信息 |
||||
|
* |
||||
|
* @param id 系统用户主键 |
||||
|
* @return 结果 |
||||
|
*/ |
||||
|
@Override |
||||
|
public int deleteUsersById(Long id) |
||||
|
{ |
||||
|
return usersMapper.deleteUsersById(id); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,66 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8" ?> |
||||
|
<!DOCTYPE mapper |
||||
|
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" |
||||
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
||||
|
<mapper namespace="com.ruoyi.system.mapper.UsersMapper"> |
||||
|
|
||||
|
<resultMap type="Users" id="UsersResult"> |
||||
|
<result property="id" column="id" /> |
||||
|
<result property="username" column="username" /> |
||||
|
<result property="password" column="password" /> |
||||
|
<result property="role" column="role" /> |
||||
|
</resultMap> |
||||
|
|
||||
|
<sql id="selectUsersVo"> |
||||
|
select id, username, password, role from users |
||||
|
</sql> |
||||
|
|
||||
|
<select id="selectUsersList" parameterType="Users" resultMap="UsersResult"> |
||||
|
<include refid="selectUsersVo"/> |
||||
|
<where> |
||||
|
<if test="username != null and username != ''"> and username like concat('%', #{username}, '%')</if> |
||||
|
<if test="password != null and password != ''"> and password = #{password}</if> |
||||
|
<if test="role != null "> and role = #{role}</if> |
||||
|
</where> |
||||
|
</select> |
||||
|
|
||||
|
<select id="selectUsersById" parameterType="Long" resultMap="UsersResult"> |
||||
|
<include refid="selectUsersVo"/> |
||||
|
where id = #{id} |
||||
|
</select> |
||||
|
|
||||
|
<insert id="insertUsers" parameterType="Users" useGeneratedKeys="true" keyProperty="id"> |
||||
|
insert into users |
||||
|
<trim prefix="(" suffix=")" suffixOverrides=","> |
||||
|
<if test="username != null and username != ''">username,</if> |
||||
|
<if test="password != null and password != ''">password,</if> |
||||
|
<if test="role != null">role,</if> |
||||
|
</trim> |
||||
|
<trim prefix="values (" suffix=")" suffixOverrides=","> |
||||
|
<if test="username != null and username != ''">#{username},</if> |
||||
|
<if test="password != null and password != ''">#{password},</if> |
||||
|
<if test="role != null">#{role},</if> |
||||
|
</trim> |
||||
|
</insert> |
||||
|
|
||||
|
<update id="updateUsers" parameterType="Users"> |
||||
|
update users |
||||
|
<trim prefix="SET" suffixOverrides=","> |
||||
|
<if test="username != null and username != ''">username = #{username},</if> |
||||
|
<if test="password != null and password != ''">password = #{password},</if> |
||||
|
<if test="role != null">role = #{role},</if> |
||||
|
</trim> |
||||
|
where id = #{id} |
||||
|
</update> |
||||
|
|
||||
|
<delete id="deleteUsersById" parameterType="Long"> |
||||
|
delete from users where id = #{id} |
||||
|
</delete> |
||||
|
|
||||
|
<delete id="deleteUsersByIds" parameterType="String"> |
||||
|
delete from users where id in |
||||
|
<foreach item="id" collection="array" open="(" separator="," close=")"> |
||||
|
#{id} |
||||
|
</foreach> |
||||
|
</delete> |
||||
|
</mapper> |
||||
Binary file not shown.
@ -1,139 +0,0 @@ |
|||||
<template> |
|
||||
<div |
|
||||
class="floating-left-menu" |
|
||||
:class="{ 'hidden': isHidden }" |
|
||||
@mouseenter="showTooltip = true" |
|
||||
@mouseleave="showTooltip = false" |
|
||||
> |
|
||||
<!-- 隐藏按钮(>箭头)。 --> |
|
||||
<div class="hide-btn" @click="handleHide" title="隐藏菜单"> |
|
||||
<i class="el-icon-arrow-left"></i> |
|
||||
</div> |
|
||||
|
|
||||
<!-- 一级菜单 --> |
|
||||
<div class="menu-icons"> |
|
||||
<div |
|
||||
v-for="item in menuItems" |
|
||||
:key="item.id" |
|
||||
class="menu-item" |
|
||||
:class="{ active: activeMenu === item.id }" |
|
||||
@click="handleSelectMenu(item)" |
|
||||
:title="item.name" |
|
||||
> |
|
||||
<i :class="item.icon"></i> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<script> |
|
||||
export default { |
|
||||
name: 'LeftMenu', |
|
||||
props: { |
|
||||
isHidden: { |
|
||||
type: Boolean, |
|
||||
default: false |
|
||||
}, |
|
||||
menuItems: { |
|
||||
type: Array, |
|
||||
default: () => [] |
|
||||
}, |
|
||||
activeMenu: { |
|
||||
type: String, |
|
||||
default: '' |
|
||||
} |
|
||||
}, |
|
||||
methods: { |
|
||||
handleHide() { |
|
||||
this.$emit('hide') |
|
||||
}, |
|
||||
|
|
||||
handleSelectMenu(item) { |
|
||||
this.$emit('select', item) |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<style scoped> |
|
||||
/* 左侧菜单栏 - 蓝色主题 。*/ |
|
||||
.floating-left-menu { |
|
||||
position: absolute; |
|
||||
top: 70px; |
|
||||
left: 20px; |
|
||||
width: 40px; |
|
||||
background: rgba(255, 255, 255, 0.3); |
|
||||
backdrop-filter: blur(10px); |
|
||||
border: 1px solid rgba(0, 138, 255, 0.1); |
|
||||
border-radius: 8px; |
|
||||
z-index: 90; |
|
||||
box-shadow: 0 4px 12px rgba(0, 138, 255, 0.2); |
|
||||
padding: 15px 5px; |
|
||||
transition: all 0.3s ease; |
|
||||
overflow: hidden; |
|
||||
opacity: 1; |
|
||||
transform: translateX(0); |
|
||||
} |
|
||||
|
|
||||
.floating-left-menu.hidden { |
|
||||
opacity: 0; |
|
||||
transform: translateX(-100%); |
|
||||
pointer-events: none; |
|
||||
} |
|
||||
|
|
||||
.hide-btn { |
|
||||
position: absolute; |
|
||||
top: 15px; |
|
||||
left: 50%; |
|
||||
transform: translateX(-50%); |
|
||||
width: 24px; |
|
||||
height: 24px; |
|
||||
display: flex; |
|
||||
justify-content: center; |
|
||||
align-items: center; |
|
||||
cursor: pointer; |
|
||||
color: #008aff; |
|
||||
font-size: 16px; |
|
||||
transition: all 0.3s; |
|
||||
background: rgba(255, 255, 255, 0.5); |
|
||||
border-radius: 4px; |
|
||||
z-index: 10; |
|
||||
} |
|
||||
|
|
||||
.menu-icons { |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
gap: 10px; |
|
||||
margin-top: 30px; |
|
||||
} |
|
||||
|
|
||||
.menu-item { |
|
||||
display: flex; |
|
||||
justify-content: center; |
|
||||
align-items: center; |
|
||||
height: 40px; |
|
||||
cursor: pointer; |
|
||||
color: #555; |
|
||||
font-size: 20px; |
|
||||
position: relative; |
|
||||
transition: all 0.3s; |
|
||||
border-radius: 4px; |
|
||||
padding: 0 5px; |
|
||||
background: rgba(255, 255, 255, 0.8); |
|
||||
backdrop-filter: blur(5px); |
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
|
||||
} |
|
||||
|
|
||||
.menu-item:hover { |
|
||||
background: rgba(0, 138, 255, 0.1); |
|
||||
color: #008aff; |
|
||||
transform: translateY(-2px); |
|
||||
box-shadow: 0 4px 10px rgba(0, 138, 255, 0.2); |
|
||||
} |
|
||||
|
|
||||
.menu-item.active { |
|
||||
background: rgba(0, 138, 255, 0.15); |
|
||||
color: #008aff; |
|
||||
box-shadow: 0 2px 8px rgba(0, 138, 255, 0.3); |
|
||||
} |
|
||||
</style> |
|
||||
@ -1,723 +0,0 @@ |
|||||
<template> |
|
||||
<div> |
|
||||
<!-- 右侧外部隐藏按钮。 --> |
|
||||
<div |
|
||||
class="right-external-hide-btn" |
|
||||
:class="{ hidden: isHidden }" |
|
||||
@click="handleHide" |
|
||||
title="隐藏右侧面板" |
|
||||
> |
|
||||
<i class="el-icon-arrow-right"></i> |
|
||||
</div> |
|
||||
|
|
||||
<!-- 右侧实体列表(浮动)- 蓝色主题 --> |
|
||||
<div |
|
||||
class="floating-right-panel blue-theme" |
|
||||
:class="{ 'hidden': isHidden }" |
|
||||
> |
|
||||
<!-- 方案内容 --> |
|
||||
<div v-if="activeTab === 'plan'" class="tab-content plan-content"> |
|
||||
<div class="section"> |
|
||||
<div class="section-title">航线列表</div> |
|
||||
<div class="route-list"> |
|
||||
<div |
|
||||
v-for="route in routes" |
|
||||
:key="route.id" |
|
||||
class="route-item" |
|
||||
:class="{ selected: selectedRouteId === route.id }" |
|
||||
@click="handleSelectRoute(route)" |
|
||||
> |
|
||||
<i class="el-icon-map-location"></i> |
|
||||
<div class="route-info"> |
|
||||
<div class="route-name">{{ route.name }}</div> |
|
||||
<div class="route-meta">{{ route.points }}个航点</div> |
|
||||
</div> |
|
||||
<el-tag |
|
||||
v-if="route.conflict" |
|
||||
size="mini" |
|
||||
type="danger" |
|
||||
class="conflict-tag" |
|
||||
> |
|
||||
冲突 |
|
||||
</el-tag> |
|
||||
<div class="route-actions"> |
|
||||
<i class="el-icon-edit" title="编辑" @click.stop="handleOpenRouteDialog(route)"></i> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
<div v-if="selectedRouteDetails" class="section"> |
|
||||
<div class="section-title">航点列表</div> |
|
||||
<div class="waypoint-list"> |
|
||||
<div |
|
||||
v-for="point in selectedRouteDetails.waypoints" |
|
||||
:key="point.name" |
|
||||
class="waypoint-item" |
|
||||
@click="handleOpenWaypointDialog(point)" |
|
||||
> |
|
||||
<i class="el-icon-location"></i> |
|
||||
<div class="waypoint-info"> |
|
||||
<div class="waypoint-name">{{ point.name }}</div> |
|
||||
<div class="waypoint-meta">高度: {{ point.altitude }}m | 速度: {{ point.speed }}</div> |
|
||||
</div> |
|
||||
<div class="waypoint-actions"> |
|
||||
<i class="el-icon-edit" title="编辑" @click.stop="handleOpenWaypointDialog(point)"></i> |
|
||||
<i class="el-icon-delete" title="删除"></i> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
<div class="action-buttons"> |
|
||||
<el-button type="primary" size="mini" icon="el-icon-circle-plus" class="blue-btn" @click="handleAddWaypoint"> |
|
||||
添加航点 |
|
||||
</el-button> |
|
||||
<el-button size="mini" class="blue-btn" @click="handleCancelRoute"> |
|
||||
取消 |
|
||||
</el-button> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
<!-- 冲突内容 --> |
|
||||
<div v-if="activeTab === 'conflict'" class="tab-content conflict-content"> |
|
||||
<div v-if="conflicts.length > 0" class="conflict-list"> |
|
||||
<div |
|
||||
v-for="conflict in conflicts" |
|
||||
:key="conflict.id" |
|
||||
class="conflict-item" |
|
||||
> |
|
||||
<div class="conflict-header"> |
|
||||
<i class="el-icon-warning" style="color: #f56c6c;"></i> |
|
||||
<span class="conflict-title">{{ conflict.title }}</span> |
|
||||
<el-tag size="mini" type="danger">严重</el-tag> |
|
||||
</div> |
|
||||
<div class="conflict-details"> |
|
||||
<div class="detail-item"> |
|
||||
<span class="label">涉及航线:</span> |
|
||||
<span class="value">{{ conflict.routes.join('、') }}</span> |
|
||||
</div> |
|
||||
<div class="detail-item"> |
|
||||
<span class="label">冲突时间:</span> |
|
||||
<span class="value">{{ conflict.time }}</span> |
|
||||
</div> |
|
||||
<div class="detail-item"> |
|
||||
<span class="label">冲突位置:</span> |
|
||||
<span class="value">{{ conflict.position }}</span> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="conflict-actions"> |
|
||||
<el-button type="text" size="mini" class="blue-text-btn" @click="handleViewConflict(conflict)"> |
|
||||
查看详情 |
|
||||
</el-button> |
|
||||
<el-button type="text" size="mini" class="blue-text-btn" @click="handleResolveConflict(conflict)"> |
|
||||
解决冲突 |
|
||||
</el-button> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div v-else class="no-conflict"> |
|
||||
<i class="el-icon-success" style="color: #67c23a; font-size: 24px;"></i> |
|
||||
<p>暂无冲突</p> |
|
||||
<el-button size="mini" class="blue-btn" @click="handleRunConflictCheck"> |
|
||||
重新检测 |
|
||||
</el-button> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
<!-- 平台内容 --> |
|
||||
<div v-if="activeTab === 'platform'" class="tab-content platform-content"> |
|
||||
<div class="platform-categories"> |
|
||||
<el-tabs v-model="activePlatformTab" type="card" size="mini" class="blue-tabs"> |
|
||||
<el-tab-pane label="空中" name="air"> |
|
||||
<div class="platform-list"> |
|
||||
<div |
|
||||
v-for="platform in airPlatforms" |
|
||||
:key="platform.id" |
|
||||
class="platform-item" |
|
||||
@click="handleOpenPlatformDialog(platform)" |
|
||||
> |
|
||||
<div class="platform-icon" :style="{ color: platform.color }"> |
|
||||
<i :class="platform.icon"></i> |
|
||||
</div> |
|
||||
<div class="platform-info"> |
|
||||
<div class="platform-name">{{ platform.name }}</div> |
|
||||
<div class="platform-type">{{ platform.type }}</div> |
|
||||
</div> |
|
||||
<div class="platform-status"> |
|
||||
<span class="status-dot" :class="platform.status"></span> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</el-tab-pane> |
|
||||
|
|
||||
<el-tab-pane label="海上" name="sea"> |
|
||||
<div class="platform-list"> |
|
||||
<div |
|
||||
v-for="platform in seaPlatforms" |
|
||||
:key="platform.id" |
|
||||
class="platform-item" |
|
||||
@click="handleOpenPlatformDialog(platform)" |
|
||||
> |
|
||||
<div class="platform-icon" :style="{ color: platform.color }"> |
|
||||
<i :class="platform.icon"></i> |
|
||||
</div> |
|
||||
<div class="platform-info"> |
|
||||
<div class="platform-name">{{ platform.name }}</div> |
|
||||
<div class="platform-type">{{ platform.type }}</div> |
|
||||
</div> |
|
||||
<div class="platform-status"> |
|
||||
<span class="status-dot" :class="platform.status"></span> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</el-tab-pane> |
|
||||
|
|
||||
<el-tab-pane label="地面" name="ground"> |
|
||||
<div class="platform-list"> |
|
||||
<div |
|
||||
v-for="platform in groundPlatforms" |
|
||||
:key="platform.id" |
|
||||
class="platform-item" |
|
||||
@click="handleOpenPlatformDialog(platform)" |
|
||||
> |
|
||||
<div class="platform-icon" :style="{ color: platform.color }"> |
|
||||
<i :class="platform.icon"></i> |
|
||||
</div> |
|
||||
<div class="platform-info"> |
|
||||
<div class="platform-name">{{ platform.name }}</div> |
|
||||
<div class="platform-type">{{ platform.type }}</div> |
|
||||
</div> |
|
||||
<div class="platform-status"> |
|
||||
<span class="status-dot" :class="platform.status"></span> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</el-tab-pane> |
|
||||
</el-tabs> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<script> |
|
||||
export default { |
|
||||
name: 'RightPanel', |
|
||||
props: { |
|
||||
isHidden: { |
|
||||
type: Boolean, |
|
||||
default: false |
|
||||
}, |
|
||||
activeTab: { |
|
||||
type: String, |
|
||||
default: 'plan' |
|
||||
}, |
|
||||
routes: { |
|
||||
type: Array, |
|
||||
default: () => [] |
|
||||
}, |
|
||||
selectedRouteId: { |
|
||||
type: [String, Number], |
|
||||
default: null |
|
||||
}, |
|
||||
selectedRouteDetails: { |
|
||||
type: Object, |
|
||||
default: null |
|
||||
}, |
|
||||
conflicts: { |
|
||||
type: Array, |
|
||||
default: () => [] |
|
||||
}, |
|
||||
conflictCount: { |
|
||||
type: Number, |
|
||||
default: 0 |
|
||||
}, |
|
||||
airPlatforms: { |
|
||||
type: Array, |
|
||||
default: () => [] |
|
||||
}, |
|
||||
seaPlatforms: { |
|
||||
type: Array, |
|
||||
default: () => [] |
|
||||
}, |
|
||||
groundPlatforms: { |
|
||||
type: Array, |
|
||||
default: () => [] |
|
||||
} |
|
||||
}, |
|
||||
data() { |
|
||||
return { |
|
||||
activePlatformTab: 'air' |
|
||||
} |
|
||||
}, |
|
||||
methods: { |
|
||||
handleHide() { |
|
||||
this.$emit('hide') |
|
||||
}, |
|
||||
|
|
||||
handleSelectRoute(route) { |
|
||||
this.$emit('select-route', route) |
|
||||
}, |
|
||||
|
|
||||
handleOpenRouteDialog(route) { |
|
||||
this.$emit('open-route-dialog', route) |
|
||||
}, |
|
||||
|
|
||||
handleOpenWaypointDialog(point) { |
|
||||
this.$emit('open-waypoint-dialog', point) |
|
||||
}, |
|
||||
|
|
||||
handleAddWaypoint() { |
|
||||
this.$emit('add-waypoint') |
|
||||
}, |
|
||||
|
|
||||
handleCancelRoute() { |
|
||||
this.$emit('cancel-route') |
|
||||
}, |
|
||||
|
|
||||
handleViewConflict(conflict) { |
|
||||
this.$emit('view-conflict', conflict) |
|
||||
}, |
|
||||
|
|
||||
handleResolveConflict(conflict) { |
|
||||
this.$emit('resolve-conflict', conflict) |
|
||||
}, |
|
||||
|
|
||||
handleRunConflictCheck() { |
|
||||
this.$emit('run-conflict-check') |
|
||||
}, |
|
||||
|
|
||||
handleOpenPlatformDialog(platform) { |
|
||||
this.$emit('open-platform-dialog', platform) |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<style scoped> |
|
||||
/* 右侧外部隐藏按钮 */ |
|
||||
.right-external-hide-btn { |
|
||||
position: absolute; |
|
||||
top: 80px; |
|
||||
right: 20px; |
|
||||
width: 30px; |
|
||||
height: 30px; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
cursor: pointer; |
|
||||
color: #008aff; |
|
||||
font-size: 18px; |
|
||||
background: rgba(255, 255, 255, 0.5); |
|
||||
border-radius: 50%; |
|
||||
z-index: 85; |
|
||||
box-shadow: 0 2px 8px rgba(0, 138, 255, 0.3); |
|
||||
transition: all 0.3s; |
|
||||
backdrop-filter: blur(5px); |
|
||||
} |
|
||||
|
|
||||
.right-external-hide-btn:hover { |
|
||||
color: #0066cc; |
|
||||
background: rgba(0, 138, 255, 0.2); |
|
||||
transform: scale(1.1); |
|
||||
} |
|
||||
|
|
||||
.right-external-hide-btn.hidden { |
|
||||
opacity: 0; |
|
||||
transform: translateX(100%); |
|
||||
pointer-events: none; |
|
||||
} |
|
||||
|
|
||||
/* 右侧浮动面板 - 蓝色主题 */ |
|
||||
.floating-right-panel { |
|
||||
position: absolute; |
|
||||
top: 70px; |
|
||||
right: 20px; |
|
||||
width: 300px; |
|
||||
border-radius: 0 8px 8px 8px; |
|
||||
z-index: 90; |
|
||||
color: #333; |
|
||||
overflow: hidden; |
|
||||
box-shadow: 0 4px 20px rgba(0, 138, 255, 0.2); |
|
||||
background: rgba(255, 255, 255, 0.95); |
|
||||
backdrop-filter: blur(15px); |
|
||||
transition: all 0.3s ease; |
|
||||
opacity: 1; |
|
||||
transform: translateX(0); |
|
||||
} |
|
||||
|
|
||||
.floating-right-panel.hidden { |
|
||||
opacity: 0; |
|
||||
transform: translateX(100%); |
|
||||
pointer-events: none; |
|
||||
} |
|
||||
|
|
||||
.tab-content { |
|
||||
padding: 15px; |
|
||||
max-height: 500px; |
|
||||
overflow-y: auto; |
|
||||
background: rgba(255, 255, 255, 0.95); |
|
||||
backdrop-filter: blur(10px); |
|
||||
} |
|
||||
|
|
||||
.section { |
|
||||
margin-bottom: 20px; |
|
||||
} |
|
||||
|
|
||||
.section-title { |
|
||||
font-size: 14px; |
|
||||
font-weight: 600; |
|
||||
color: #008aff; |
|
||||
margin-bottom: 10px; |
|
||||
padding-bottom: 8px; |
|
||||
border-bottom: 2px solid rgba(0, 138, 255, 0.2); |
|
||||
} |
|
||||
|
|
||||
.route-list { |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
gap: 8px; |
|
||||
} |
|
||||
|
|
||||
.route-item { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
gap: 10px; |
|
||||
padding: 10px; |
|
||||
background: rgba(255, 255, 255, 0.8); |
|
||||
border-radius: 6px; |
|
||||
cursor: pointer; |
|
||||
transition: all 0.3s; |
|
||||
border: 1px solid rgba(0, 138, 255, 0.1); |
|
||||
position: relative; |
|
||||
} |
|
||||
|
|
||||
.route-item:hover { |
|
||||
background: rgba(0, 138, 255, 0.1); |
|
||||
transform: translateX(-2px); |
|
||||
box-shadow: 0 2px 8px rgba(0, 138, 255, 0.15); |
|
||||
} |
|
||||
|
|
||||
.route-item.selected { |
|
||||
background: rgba(0, 138, 255, 0.15); |
|
||||
border-color: rgba(0, 138, 255, 0.3); |
|
||||
box-shadow: 0 2px 10px rgba(0, 138, 255, 0.25); |
|
||||
} |
|
||||
|
|
||||
.route-info { |
|
||||
flex: 1; |
|
||||
} |
|
||||
|
|
||||
.route-name { |
|
||||
font-size: 14px; |
|
||||
font-weight: 500; |
|
||||
color: #333; |
|
||||
} |
|
||||
|
|
||||
.route-meta { |
|
||||
font-size: 12px; |
|
||||
color: #999; |
|
||||
} |
|
||||
|
|
||||
.route-actions { |
|
||||
display: flex; |
|
||||
gap: 8px; |
|
||||
} |
|
||||
|
|
||||
.route-actions i { |
|
||||
cursor: pointer; |
|
||||
color: #008aff; |
|
||||
font-size: 14px; |
|
||||
padding: 4px; |
|
||||
border-radius: 4px; |
|
||||
transition: all 0.2s; |
|
||||
} |
|
||||
|
|
||||
.route-actions i:hover { |
|
||||
background: rgba(0, 138, 255, 0.1); |
|
||||
transform: scale(1.2); |
|
||||
} |
|
||||
|
|
||||
.waypoint-list { |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
gap: 8px; |
|
||||
} |
|
||||
|
|
||||
.waypoint-item { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
gap: 10px; |
|
||||
padding: 10px; |
|
||||
background: rgba(255, 255, 255, 0.8); |
|
||||
border-radius: 6px; |
|
||||
cursor: pointer; |
|
||||
transition: all 0.3s; |
|
||||
border: 1px solid rgba(0, 138, 255, 0.1); |
|
||||
} |
|
||||
|
|
||||
.waypoint-item:hover { |
|
||||
background: rgba(0, 138, 255, 0.1); |
|
||||
transform: translateX(-2px); |
|
||||
box-shadow: 0 2px 8px rgba(0, 138, 255, 0.15); |
|
||||
} |
|
||||
|
|
||||
.waypoint-info { |
|
||||
flex: 1; |
|
||||
} |
|
||||
|
|
||||
.waypoint-name { |
|
||||
font-size: 14px; |
|
||||
font-weight: 500; |
|
||||
color: #333; |
|
||||
} |
|
||||
|
|
||||
.waypoint-meta { |
|
||||
font-size: 12px; |
|
||||
color: #999; |
|
||||
} |
|
||||
|
|
||||
.waypoint-actions { |
|
||||
display: flex; |
|
||||
gap: 8px; |
|
||||
} |
|
||||
|
|
||||
.waypoint-actions i { |
|
||||
cursor: pointer; |
|
||||
color: #008aff; |
|
||||
font-size: 14px; |
|
||||
padding: 4px; |
|
||||
border-radius: 4px; |
|
||||
transition: all 0.2s; |
|
||||
} |
|
||||
|
|
||||
.waypoint-actions i:hover { |
|
||||
background: rgba(0, 138, 255, 0.1); |
|
||||
transform: scale(1.2); |
|
||||
} |
|
||||
|
|
||||
.action-buttons { |
|
||||
display: flex; |
|
||||
gap: 10px; |
|
||||
padding: 10px 0; |
|
||||
} |
|
||||
|
|
||||
.conflict-list { |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
gap: 12px; |
|
||||
} |
|
||||
|
|
||||
.conflict-item { |
|
||||
background: rgba(255, 255, 255, 0.8); |
|
||||
border-radius: 6px; |
|
||||
padding: 12px; |
|
||||
border: 1px solid rgba(245, 108, 108, 0.2); |
|
||||
transition: all 0.3s; |
|
||||
} |
|
||||
|
|
||||
.conflict-item:hover { |
|
||||
background: rgba(245, 108, 108, 0.1); |
|
||||
box-shadow: 0 2px 8px rgba(245, 108, 108, 0.15); |
|
||||
} |
|
||||
|
|
||||
.conflict-header { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
gap: 8px; |
|
||||
margin-bottom: 10px; |
|
||||
} |
|
||||
|
|
||||
.conflict-title { |
|
||||
flex: 1; |
|
||||
font-weight: 600; |
|
||||
color: #f56c6c; |
|
||||
} |
|
||||
|
|
||||
.conflict-details { |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
gap: 8px; |
|
||||
margin-bottom: 10px; |
|
||||
} |
|
||||
|
|
||||
.detail-item { |
|
||||
display: flex; |
|
||||
gap: 8px; |
|
||||
font-size: 13px; |
|
||||
} |
|
||||
|
|
||||
.detail-item .label { |
|
||||
color: #999; |
|
||||
min-width: 70px; |
|
||||
} |
|
||||
|
|
||||
.detail-item .value { |
|
||||
color: #333; |
|
||||
font-weight: 500; |
|
||||
} |
|
||||
|
|
||||
.conflict-actions { |
|
||||
display: flex; |
|
||||
gap: 10px; |
|
||||
} |
|
||||
|
|
||||
.no-conflict { |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
align-items: center; |
|
||||
gap: 15px; |
|
||||
padding: 40px 20px; |
|
||||
color: #999; |
|
||||
} |
|
||||
|
|
||||
.platform-categories { |
|
||||
height: 100%; |
|
||||
} |
|
||||
|
|
||||
.platform-list { |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
gap: 10px; |
|
||||
max-height: 450px; |
|
||||
overflow-y: auto; |
|
||||
} |
|
||||
|
|
||||
.platform-item { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
gap: 12px; |
|
||||
padding: 12px; |
|
||||
background: rgba(255, 255, 255, 0.8); |
|
||||
border-radius: 6px; |
|
||||
cursor: pointer; |
|
||||
transition: all 0.3s; |
|
||||
border: 1px solid rgba(0, 138, 255, 0.1); |
|
||||
} |
|
||||
|
|
||||
.platform-item:hover { |
|
||||
background: rgba(0, 138, 255, 0.1); |
|
||||
transform: translateX(-2px); |
|
||||
box-shadow: 0 2px 8px rgba(0, 138, 255, 0.15); |
|
||||
} |
|
||||
|
|
||||
.platform-icon { |
|
||||
width: 40px; |
|
||||
height: 40px; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
font-size: 20px; |
|
||||
background: rgba(255, 255, 255, 0.9); |
|
||||
border-radius: 50%; |
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); |
|
||||
} |
|
||||
|
|
||||
.platform-info { |
|
||||
flex: 1; |
|
||||
} |
|
||||
|
|
||||
.platform-name { |
|
||||
font-size: 14px; |
|
||||
font-weight: 500; |
|
||||
color: #333; |
|
||||
} |
|
||||
|
|
||||
.platform-type { |
|
||||
font-size: 12px; |
|
||||
color: #999; |
|
||||
} |
|
||||
|
|
||||
.platform-status { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
} |
|
||||
|
|
||||
.status-dot { |
|
||||
width: 8px; |
|
||||
height: 8px; |
|
||||
border-radius: 50%; |
|
||||
display: inline-block; |
|
||||
} |
|
||||
|
|
||||
.status-dot.online { |
|
||||
background: #67c23a; |
|
||||
box-shadow: 0 0 6px rgba(103, 194, 58, 0.6); |
|
||||
} |
|
||||
|
|
||||
.status-dot.offline { |
|
||||
background: #999; |
|
||||
} |
|
||||
|
|
||||
.status-dot.operating { |
|
||||
background: #008aff; |
|
||||
animation: pulse 2s infinite; |
|
||||
box-shadow: 0 0 10px rgba(0, 138, 255, 0.8); |
|
||||
} |
|
||||
|
|
||||
@keyframes pulse { |
|
||||
0%, 100% { |
|
||||
opacity: 1; |
|
||||
} |
|
||||
50% { |
|
||||
opacity: 0.5; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.blue-btn { |
|
||||
background: rgba(0, 138, 255, 0.1); |
|
||||
color: #008aff; |
|
||||
border: 1px solid rgba(0, 138, 255, 0.3); |
|
||||
} |
|
||||
|
|
||||
.blue-btn:hover { |
|
||||
background: rgba(0, 138, 255, 0.2); |
|
||||
border-color: rgba(0, 138, 255, 0.5); |
|
||||
} |
|
||||
|
|
||||
.blue-text-btn { |
|
||||
color: #008aff; |
|
||||
} |
|
||||
|
|
||||
.blue-text-btn:hover { |
|
||||
color: #0066cc; |
|
||||
} |
|
||||
|
|
||||
.blue-badge { |
|
||||
background: rgba(245, 108, 108, 0.1); |
|
||||
color: #f56c6c; |
|
||||
border: 1px solid rgba(245, 108, 108, 0.3); |
|
||||
} |
|
||||
|
|
||||
.blue-tabs >>> .el-tabs__item { |
|
||||
color: #666; |
|
||||
transition: all 0.3s; |
|
||||
} |
|
||||
|
|
||||
.blue-tabs >>> .el-tabs__item:hover { |
|
||||
color: #008aff; |
|
||||
} |
|
||||
|
|
||||
.blue-tabs >>> .el-tabs__item.is-active { |
|
||||
color: #008aff; |
|
||||
font-weight: 600; |
|
||||
} |
|
||||
|
|
||||
.blue-tabs >>> .el-tabs__active-bar { |
|
||||
background-color: #008aff; |
|
||||
box-shadow: 0 0 6px rgba(0, 138, 255, 0.5); |
|
||||
} |
|
||||
|
|
||||
.blue-tabs >>> .el-tabs__nav-wrap::after { |
|
||||
background-color: rgba(0, 138, 255, 0.3); |
|
||||
} |
|
||||
|
|
||||
.blue-success { |
|
||||
color: #67c23a; |
|
||||
} |
|
||||
|
|
||||
.blue-warning { |
|
||||
color: #e6a23c; |
|
||||
} |
|
||||
</style> |
|
||||
Loading…
Reference in new issue