23 changed files with 400 additions and 95 deletions
Binary file not shown.
@ -1 +0,0 @@ |
|||||
## Hello World |
|
@ -0,0 +1,8 @@ |
|||||
|
# 欢迎做客前端作坊 |
||||
|
|
||||
|
> 作者:YSir |
||||
|
> 站点:[个人单页](https://xieyaxin.top) |
||||
|
|
||||
|
## 前言 |
||||
|
|
||||
|
又是一个新项目,工作这些年,也不知做了多少项目,大多数是难产而亡,本项目是第一个比较完整的后端项目,开发此站的目的仅是学习学习hapijs,最后写着写着就不想再搞其他的框架了,折腾累了,对于繁杂的网络信息,我感到神经有些疲劳,因此就先开发着这个项目,把完整的开源项目流程走一遍。 |
@ -0,0 +1,124 @@ |
|||||
|
.tree{ |
||||
|
flex: 1; |
||||
|
direction: rtl; |
||||
|
overflow: auto; |
||||
|
padding: 4px 0; |
||||
|
position: relative; |
||||
|
width: 300px; |
||||
|
font-size: .875em; |
||||
|
font-weight: 400; |
||||
|
color: rgba(25, 23, 17, 0.6); |
||||
|
fill: rgba(25, 23, 17, 0.6); |
||||
|
z-index: 0; |
||||
|
user-select: none; |
||||
|
/* @include media-pc(sm){ |
||||
|
width: 100%; |
||||
|
} */ |
||||
|
} |
||||
|
|
||||
|
/*去掉前面默认的小黑三角*/ |
||||
|
.tree details summary::-webkit-details-marker{ |
||||
|
display:none; |
||||
|
} |
||||
|
|
||||
|
.tree summary{ |
||||
|
outline: 0; |
||||
|
padding-left: 17px; |
||||
|
list-style: none; |
||||
|
list-style-type: none; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
display: -webkit-flex; |
||||
|
-webkit-line-clamp: 1; |
||||
|
-webkit-box-orient: vertical; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
|
||||
|
.tree .tree-md-file:before{ |
||||
|
content: ''; |
||||
|
background: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNjczNDAyMTUyMTkwIiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEyODAgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjYyMzIiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjUwIiBoZWlnaHQ9IjIwMCI+PHBhdGggZD0iTTExODcuNiAxMTguMkg5Mi40QzQxLjQgMTE4LjIgMCAxNTkuNiAwIDIxMC40djYwM2MwIDUxIDQxLjQgOTIuNCA5Mi40IDkyLjRoMTA5NS40YzUxIDAgOTIuNC00MS40IDkyLjItOTIuMlYyMTAuNGMwLTUwLjgtNDEuNC05Mi4yLTkyLjQtOTIuMnpNNjc3IDcyMS4ySDU1NHYtMjQwbC0xMjMgMTUzLjgtMTIzLTE1My44djI0MEgxODQuNlYzMDIuOGgxMjNsMTIzIDE1My44IDEyMy0xNTMuOGgxMjN2NDE4LjR6IG0yNzAuNiA2LjJMNzYzIDUxMkg4ODZWMzAyLjhoMTIzVjUxMkgxMTMyeiIgcC1pZD0iNjIzMyIgZmlsbD0iIzhjOGM4YyI+PC9wYXRoPjwvc3ZnPg==") center no-repeat !important; |
||||
|
background-size: contain !important; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
.tree .tree-md-file.mdx-file:before{ |
||||
|
content: ''; |
||||
|
background: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNjczNDAyMTUyMTkwIiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEyODAgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjYyMzIiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjUwIiBoZWlnaHQ9IjIwMCI+PHBhdGggZD0iTTExODcuNiAxMTguMkg5Mi40QzQxLjQgMTE4LjIgMCAxNTkuNiAwIDIxMC40djYwM2MwIDUxIDQxLjQgOTIuNCA5Mi40IDkyLjRoMTA5NS40YzUxIDAgOTIuNC00MS40IDkyLjItOTIuMlYyMTAuNGMwLTUwLjgtNDEuNC05Mi4yLTkyLjQtOTIuMnpNNjc3IDcyMS4ySDU1NHYtMjQwbC0xMjMgMTUzLjgtMTIzLTE1My44djI0MEgxODQuNlYzMDIuOGgxMjNsMTIzIDE1My44IDEyMy0xNTMuOGgxMjN2NDE4LjR6IG0yNzAuNiA2LjJMNzYzIDUxMkg4ODZWMzAyLjhoMTIzVjUxMkgxMTMyeiIgcC1pZD0iNjIzMyIgZmlsbD0iIzhjOGM4YyI+PC9wYXRoPjwvc3ZnPg==") center no-repeat !important; |
||||
|
background-size: contain !important; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
.tree summary:not(:only-child){ |
||||
|
background: url("data:image/svg+xml,%3Csvg width='12' height='12' viewBox='0 0 12 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M5.354 2.646A.5.5 0 0 0 4.5 3v6a.5.5 0 0 0 .854.354l3-3a.5.5 0 0 0 0-.708l-3-3z' fill='%23000' fill-opacity='.45'/%3E%3C/svg%3E") 4px center no-repeat; |
||||
|
} |
||||
|
.tree details[open]>summary:not(:only-child){ |
||||
|
background-image: url("data:image/svg+xml,%3Csvg width='12' height='12' viewBox='0 0 12 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M9.354 5.354A.5.5 0 0 0 9 4.5H3a.5.5 0 0 0-.354.854l3 3a.5.5 0 0 0 .708 0l3-3z' fill='%23000' fill-opacity='.45'/%3E%3C/svg%3E"); |
||||
|
} |
||||
|
.tree details{ |
||||
|
padding-left: 15px |
||||
|
} |
||||
|
.tree>details{ |
||||
|
padding-left: 10px |
||||
|
} |
||||
|
summary.active{ |
||||
|
background-color: #1abc9c1a !important; |
||||
|
} |
||||
|
.op{ |
||||
|
display: none; |
||||
|
font-size: .8em; |
||||
|
margin-left: 1em; |
||||
|
margin-right: 1em; |
||||
|
} |
||||
|
summary:hover .op{ |
||||
|
display: block; |
||||
|
} |
||||
|
|
||||
|
.tree-item{ |
||||
|
flex: 1; |
||||
|
width: 0; |
||||
|
text-overflow: ellipsis; |
||||
|
white-space: nowrap; |
||||
|
overflow: hidden; |
||||
|
display: inline-flex; |
||||
|
align-items: center; |
||||
|
line-height: 1; |
||||
|
padding: 5px 0; |
||||
|
cursor: default; |
||||
|
} |
||||
|
.tree details summary a{ |
||||
|
border: 0; |
||||
|
color: inherit; |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
.tree-item:hover{ |
||||
|
text-decoration: none; |
||||
|
} |
||||
|
|
||||
|
.tree-item::after{ |
||||
|
content: ''; |
||||
|
position: absolute; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
height: 28px; |
||||
|
z-index: -1; |
||||
|
transition: .2s; |
||||
|
} |
||||
|
.tree-item:hover::after{ |
||||
|
background: #1abc9c1a; |
||||
|
} |
||||
|
.tree-item.active::after{ |
||||
|
background: #1abc9c48; |
||||
|
border-right: 4px solid #1abc9c; |
||||
|
} |
||||
|
|
||||
|
.tree-item::before{ |
||||
|
content: ''; |
||||
|
width: 20px; |
||||
|
height: 20px; |
||||
|
flex-shrink: 0; |
||||
|
margin-right: 5px; |
||||
|
background: url("data:image/svg+xml,%3Csvg width='20' height='20' viewBox='0 0 20 20' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M.833 3.75c0-.92.746-1.667 1.667-1.667h5.417c.247 0 .481.11.64.3l1.833 2.2h7.11c.92 0 1.667.747 1.667 1.667v10c0 .92-.747 1.667-1.667 1.667h-15c-.92 0-1.667-.746-1.667-1.667V3.75zm6.693 0H2.5v4.584h15V6.25H10a.833.833 0 0 1-.64-.3l-1.834-2.2zM17.5 10h-15v6.25h15V10z' fill='%23000' fill-opacity='.45'/%3E%3C/svg%3E") center no-repeat; |
||||
|
} |
||||
|
.tree details[open]>summary:not(:only-child)>.tree-item::before{ |
||||
|
background-image: url("data:image/svg+xml,%3Csvg width='20' height='20' viewBox='0 0 20 20' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M7.917 2.083c.247 0 .481.11.64.3l1.833 2.2h5.443c.92 0 1.667.747 1.667 1.667v1.667h.833a.833.833 0 0 1 .817.997l-1.666 8.333a.833.833 0 0 1-.817.67H1.677a.814.814 0 0 1-.157-.013.83.83 0 0 1-.687-.82V3.75c0-.92.746-1.667 1.667-1.667h5.417zM10 6.25a.833.833 0 0 1-.64-.3l-1.834-2.2H2.5v6.564l.441-1.766a.833.833 0 0 1 .809-.631h12.083V6.25H10zm-7.266 10L4.4 9.584h12.916l-1.334 6.666H2.733z' fill='%23000' fill-opacity='.45'/%3E%3C/svg%3E"); |
||||
|
} |
@ -0,0 +1,26 @@ |
|||||
|
html,body{ |
||||
|
height: 100%; |
||||
|
} |
||||
|
|
||||
|
body{ |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
} |
||||
|
|
||||
|
.section{ |
||||
|
height: 0; |
||||
|
flex-grow: 1; |
||||
|
} |
||||
|
|
||||
|
.container{ |
||||
|
height: 100%; |
||||
|
} |
||||
|
|
||||
|
#root{ |
||||
|
height: 100%; |
||||
|
} |
||||
|
|
||||
|
.bytemd { |
||||
|
height: 100%; |
||||
|
z-index: 99; |
||||
|
} |
@ -1,19 +1,19 @@ |
|||||
/home/topuser/Code/@project/hapi-demo/source/route/htmx对应路径: |
D:\1XYX\pro\hapi-demo\source\route\htmx对应路径: |
||||
不需权限(提供无需验证): GET /htmx/path/{path*} |
不需权限(提供无需验证try): GET /htmx/path/{path*} |
||||
/home/topuser/Code/@project/hapi-demo/source/route/views对应路径: |
D:\1XYX\pro\hapi-demo\source\route\views对应路径: |
||||
不需权限(提供无需验证): GET /404 |
不需权限(提供无需验证try): GET /404 |
||||
不需权限(提供无需验证): GET / |
不需权限(提供即需验证optional): GET / |
||||
不需权限(提供无需验证): GET /about |
不需权限(提供无需验证try): GET /about |
||||
需要权限 : POST /color/del |
需要权限 : POST /color/del |
||||
需要权限 : POST /color |
需要权限 : POST /color |
||||
不需权限(提供无需验证): GET /color |
不需权限(提供无需验证try): GET /color |
||||
需要权限 : GET /docs/{path*} |
需要权限 : GET /docs/{path*} |
||||
不需权限(提供无需验证): GET /{path*} |
不需权限(提供无需验证try): GET /{path*} |
||||
不需权限(提供无需验证): GET /login |
不需权限(提供无需验证try): GET /login |
||||
不需权限(提供无需验证): POST /login |
不需权限(提供无需验证try): POST /login |
||||
不需权限(提供无需验证): GET /nav |
不需权限(提供无需验证try): GET /nav |
||||
不需权限(提供无需验证): GET /register |
不需权限(提供无需验证try): GET /register |
||||
不需权限(提供无需验证): POST /register |
不需权限(提供无需验证try): POST /register |
||||
需要权限 : POST /user |
需要权限 : POST /user |
||||
需要权限 : GET /user |
需要权限 : GET /user |
||||
需要权限 : GET /user/logout |
需要权限 : GET /user/logout |
||||
|
@ -0,0 +1,56 @@ |
|||||
|
import { Sequelize, DataTypes, Optional, Model } from "sequelize" |
||||
|
|
||||
|
interface ColorCateAttributes { |
||||
|
id: number |
||||
|
name: string |
||||
|
alias: string |
||||
|
describe: string |
||||
|
|
||||
|
createdAt?: Date |
||||
|
updatedAt?: Date |
||||
|
deletedAt?: Date |
||||
|
} |
||||
|
|
||||
|
export interface ColorCateInput extends Optional<ColorCateAttributes, "id" | "alias" | "describe"> {} |
||||
|
export interface ColorCateOutput extends Required<ColorCateAttributes> {} |
||||
|
export type TColorCateModel = ReturnType<typeof ColorCateModel> |
||||
|
|
||||
|
export default function ColorCateModel(sequelize: Sequelize) { |
||||
|
interface ColorCateInstance extends Model<ColorCateAttributes, ColorCateInput>, ColorCateAttributes {} |
||||
|
const ColorCate = sequelize.define<ColorCateInstance>( |
||||
|
"color_cate", |
||||
|
{ |
||||
|
id: { |
||||
|
type: DataTypes.INTEGER, |
||||
|
autoIncrement: true, |
||||
|
primaryKey: true, |
||||
|
}, |
||||
|
name: { |
||||
|
type: DataTypes.STRING, |
||||
|
allowNull: false, |
||||
|
}, |
||||
|
alias: { |
||||
|
type: DataTypes.STRING, |
||||
|
}, |
||||
|
describe: { |
||||
|
type: DataTypes.STRING, |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
underscored: true, |
||||
|
deletedAt: true, |
||||
|
paranoid: true, |
||||
|
timestamps: true, |
||||
|
}, |
||||
|
) |
||||
|
// 覆盖Color的toJSON方法
|
||||
|
ColorCate.prototype.toJSON = function () { |
||||
|
const values = Object.assign({}, this.get()) as ColorCateAttributes |
||||
|
delete values.deletedAt |
||||
|
return values |
||||
|
} |
||||
|
ColorCate.associate = function (models) { |
||||
|
|
||||
|
} |
||||
|
return ColorCate |
||||
|
} |
@ -1,64 +1,63 @@ |
|||||
//- 服务器反馈UI |
//- 服务器反馈UI |
||||
include @/helper/helper.pug |
include @/helper/helper.pug |
||||
if flash |
if flash |
||||
.message-container |
- index = 0 |
||||
- index = 0 |
if flash.error |
||||
if flash.error |
each item in flash.error |
||||
each item in flash.error |
- index++ |
||||
- index++ |
.message.is-danger.animate__animated.animate__slideInRight(id="message"+index) |
||||
.message.is-danger.animate__animated.animate__slideInRight(id="message"+index) |
.message-header |
||||
.message-header |
p 错误 |
||||
p 错误 |
button.delete.messagec(aria-label='delete' data-target="message"+index) |
||||
button.delete.messagec(aria-label='delete' data-target="message"+index) |
.message-body |
||||
.message-body |
| #{item} |
||||
| #{item} |
if flash.success |
||||
if flash.success |
each item in flash.success |
||||
each item in flash.success |
- index++ |
||||
- index++ |
.message.is-success.animate__animated.animate__slideInRight(id="message"+index) |
||||
.message.is-success.animate__animated.animate__slideInRight(id="message"+index) |
.message-header |
||||
.message-header |
p 成功 |
||||
p 成功 |
button.delete.messagec(aria-label='delete' data-target="message"+index) |
||||
button.delete.messagec(aria-label='delete' data-target="message"+index) |
.message-body |
||||
.message-body |
| #{item} |
||||
| #{item} |
if flash.info |
||||
if flash.info |
each item in flash.info |
||||
each item in flash.info |
- index++ |
||||
- index++ |
.message.is-info.animate__animated.animate__slideInRight(id="message"+index) |
||||
.message.is-info.animate__animated.animate__slideInRight(id="message"+index) |
.message-header |
||||
.message-header |
p 信息 |
||||
p 信息 |
button.delete.messagec(aria-label='delete' data-target="message"+index) |
||||
button.delete.messagec(aria-label='delete' data-target="message"+index) |
.message-body |
||||
.message-body |
| #{item} |
||||
| #{item} |
if flash.warning |
||||
if flash.warning |
each item in flash.warning |
||||
each item in flash.warning |
- index++ |
||||
- index++ |
.message.is-warning.animate__animated.animate__slideInRight(id="message"+index) |
||||
.message.is-warning.animate__animated.animate__slideInRight(id="message"+index) |
.message-header |
||||
.message-header |
p 警告 |
||||
p 警告 |
button.delete.messagec(aria-label='delete' data-target="message"+index) |
||||
button.delete.messagec(aria-label='delete' data-target="message"+index) |
.message-body |
||||
.message-body |
| #{item} |
||||
| #{item} |
//- .toast-container.top-0.end-0.p-3 |
||||
//- .toast-container.top-0.end-0.p-3 |
//- each item in flash.error |
||||
//- each item in flash.error |
//- .toast.show(role='alert', aria-live='assertive', aria-atomic='true') |
||||
//- .toast.show(role='alert', aria-live='assertive', aria-atomic='true') |
//- .toast-header |
||||
//- .toast-header |
//- img.rounded.me-2(src='/public/image/icons/error.svg', alt='错误' style="width:20px;height: 20px;") |
||||
//- img.rounded.me-2(src='/public/image/icons/error.svg', alt='错误' style="width:20px;height: 20px;") |
//- strong.me-auto 提示 |
||||
//- strong.me-auto 提示 |
//- //- small.text-muted just now |
||||
//- //- small.text-muted just now |
//- button.btn-close(type='button', data-bs-dismiss='toast', aria-label='Close') |
||||
//- button.btn-close(type='button', data-bs-dismiss='toast', aria-label='Close') |
//- .toast-body. |
||||
//- .toast-body. |
//- #{item} |
||||
//- #{item} |
//- .toast-container.position-fixed.bottom-0.end-0.p-3 |
||||
//- .toast-container.position-fixed.bottom-0.end-0.p-3 |
//- #liveToast.toast(role='alert', aria-live='assertive', aria-atomic='true') |
||||
//- #liveToast.toast(role='alert', aria-live='assertive', aria-atomic='true') |
//- .toast-header |
||||
//- .toast-header |
//- img.rounded.me-2(src='...', alt='...') |
||||
//- img.rounded.me-2(src='...', alt='...') |
//- strong.me-auto Bootstrap |
||||
//- strong.me-auto Bootstrap |
//- small 11 mins ago |
||||
//- small 11 mins ago |
//- button.btn-close(type='button', data-bs-dismiss='toast', aria-label='Close') |
||||
//- button.btn-close(type='button', data-bs-dismiss='toast', aria-label='Close') |
//- .toast-body. |
||||
//- .toast-body. |
//- Hello, world! This is a toast message. |
||||
//- Hello, world! This is a toast message. |
//- ul |
||||
//- ul |
//- each item in flash.error |
||||
//- each item in flash.error |
//- li #{item} |
||||
//- li #{item} |
+script("js/common/flush.js") |
||||
+script("js/common/flush.js") |
|
||||
|
@ -1,10 +1,40 @@ |
|||||
include @/helper/flush.pug |
include @/helper/flush.pug |
||||
include @/helper/helper.pug |
include @/helper/helper.pug |
||||
|
|
||||
|
title 首页 |
||||
block var |
block var |
||||
-title="首页" // 网页标题 |
-title="首页" // 网页标题 |
||||
title 首页 |
|
||||
|
|
||||
div sad |
block page |
||||
if isLogin |
div |
||||
button(hx-target="#single-page" hx-get="/about?htmx" hx-push-url="/about" hx-trigger="click" hx-swap="innerHTML") Click Me! |
style |
||||
|
:public style/views/index.css |
||||
|
.content!= md |
||||
|
//- if isLogin |
||||
|
//- button(hx-target="#single-page" hx-get="/about?htmx" hx-push-url="/about" hx-trigger="click" hx-swap="innerHTML") Click Me! |
||||
|
|
||||
|
.tree#tree |
||||
|
details(open="true" style="direction: ltr;") |
||||
|
summary |
||||
|
span.tree-item sad |
||||
|
a.op(href="") 新建 |
||||
|
details(open="true") |
||||
|
summary |
||||
|
span.tree-item sad |
||||
|
a.op(href="") 新建 |
||||
|
details |
||||
|
summary |
||||
|
span.tree-item.tree-md-file |
||||
|
div(style="flex: 1;width: 0;white-space: nowrap;text-overflow: ellipsis;overflow: hidden;") asda |
||||
|
a.op(href="") 新建 |
||||
|
details |
||||
|
summary |
||||
|
span.tree-item.tree-md-file |
||||
|
div(style="flex: 1;width: 0;white-space: nowrap;text-overflow: ellipsis;overflow: hidden;") asda |
||||
|
a.op(href="") 新建 |
||||
|
details |
||||
|
summary |
||||
|
span.tree-item.tree-md-file.active |
||||
|
div(style="flex: 1;width: 0;white-space: nowrap;text-overflow: ellipsis;overflow: hidden;") asda |
||||
|
a.op(href="") 新建 |
||||
|
|
||||
|
@ -0,0 +1,31 @@ |
|||||
|
extends @/layout/single-page |
||||
|
|
||||
|
block head |
||||
|
link(rel="stylesheet", href="https://unpkg.com/bytemd/dist/index.css") |
||||
|
link(rel="stylesheet", href="https://unpkg.com/github-markdown-css") |
||||
|
script. |
||||
|
let process = {} |
||||
|
process.env = {} |
||||
|
process.env.NODE_ENV = 'production' |
||||
|
script(src="https://unpkg.com/bytemd") |
||||
|
script(src="https://unpkg.com/@bytemd/plugin-gfm") |
||||
|
|
||||
|
block page |
||||
|
include @/helper/flush.pug |
||||
|
style |
||||
|
:public style/views/test.css |
||||
|
#root |
||||
|
script. |
||||
|
const plugins = [bytemdPluginGfm()] |
||||
|
|
||||
|
const editor = new bytemd.Editor({ |
||||
|
target: document.getElementById('root'), |
||||
|
props: { |
||||
|
value: '# heading\n\nparagraph\n\n> blockquote', |
||||
|
plugins, |
||||
|
}, |
||||
|
}) |
||||
|
|
||||
|
editor.$on('change', (e) => { |
||||
|
editor.$set({ value: e.detail.value }) |
||||
|
}) |
Loading…
Reference in new issue