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对应路径: |
|||
不需权限(提供无需验证): GET /htmx/path/{path*} |
|||
/home/topuser/Code/@project/hapi-demo/source/route/views对应路径: |
|||
不需权限(提供无需验证): GET /404 |
|||
不需权限(提供无需验证): GET / |
|||
不需权限(提供无需验证): GET /about |
|||
D:\1XYX\pro\hapi-demo\source\route\htmx对应路径: |
|||
不需权限(提供无需验证try): GET /htmx/path/{path*} |
|||
D:\1XYX\pro\hapi-demo\source\route\views对应路径: |
|||
不需权限(提供无需验证try): GET /404 |
|||
不需权限(提供即需验证optional): GET / |
|||
不需权限(提供无需验证try): GET /about |
|||
需要权限 : POST /color/del |
|||
需要权限 : POST /color |
|||
不需权限(提供无需验证): GET /color |
|||
不需权限(提供无需验证try): GET /color |
|||
需要权限 : GET /docs/{path*} |
|||
不需权限(提供无需验证): GET /{path*} |
|||
不需权限(提供无需验证): GET /login |
|||
不需权限(提供无需验证): POST /login |
|||
不需权限(提供无需验证): GET /nav |
|||
不需权限(提供无需验证): GET /register |
|||
不需权限(提供无需验证): POST /register |
|||
不需权限(提供无需验证try): GET /{path*} |
|||
不需权限(提供无需验证try): GET /login |
|||
不需权限(提供无需验证try): POST /login |
|||
不需权限(提供无需验证try): GET /nav |
|||
不需权限(提供无需验证try): GET /register |
|||
不需权限(提供无需验证try): POST /register |
|||
需要权限 : POST /user |
|||
需要权限 : GET /user |
|||
需要权限 : 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 |
|||
include @/helper/helper.pug |
|||
if flash |
|||
.message-container |
|||
- index = 0 |
|||
if flash.error |
|||
each item in flash.error |
|||
- index++ |
|||
.message.is-danger.animate__animated.animate__slideInRight(id="message"+index) |
|||
.message-header |
|||
p 错误 |
|||
button.delete.messagec(aria-label='delete' data-target="message"+index) |
|||
.message-body |
|||
| #{item} |
|||
if flash.success |
|||
each item in flash.success |
|||
- index++ |
|||
.message.is-success.animate__animated.animate__slideInRight(id="message"+index) |
|||
.message-header |
|||
p 成功 |
|||
button.delete.messagec(aria-label='delete' data-target="message"+index) |
|||
.message-body |
|||
| #{item} |
|||
if flash.info |
|||
each item in flash.info |
|||
- index++ |
|||
.message.is-info.animate__animated.animate__slideInRight(id="message"+index) |
|||
.message-header |
|||
p 信息 |
|||
button.delete.messagec(aria-label='delete' data-target="message"+index) |
|||
.message-body |
|||
| #{item} |
|||
if flash.warning |
|||
each item in flash.warning |
|||
- index++ |
|||
.message.is-warning.animate__animated.animate__slideInRight(id="message"+index) |
|||
.message-header |
|||
p 警告 |
|||
button.delete.messagec(aria-label='delete' data-target="message"+index) |
|||
.message-body |
|||
| #{item} |
|||
//- .toast-container.top-0.end-0.p-3 |
|||
//- each item in flash.error |
|||
//- .toast.show(role='alert', aria-live='assertive', aria-atomic='true') |
|||
//- .toast-header |
|||
//- img.rounded.me-2(src='/public/image/icons/error.svg', alt='错误' style="width:20px;height: 20px;") |
|||
//- strong.me-auto 提示 |
|||
//- //- small.text-muted just now |
|||
//- button.btn-close(type='button', data-bs-dismiss='toast', aria-label='Close') |
|||
//- .toast-body. |
|||
//- #{item} |
|||
//- .toast-container.position-fixed.bottom-0.end-0.p-3 |
|||
//- #liveToast.toast(role='alert', aria-live='assertive', aria-atomic='true') |
|||
//- .toast-header |
|||
//- img.rounded.me-2(src='...', alt='...') |
|||
//- strong.me-auto Bootstrap |
|||
//- small 11 mins ago |
|||
//- button.btn-close(type='button', data-bs-dismiss='toast', aria-label='Close') |
|||
//- .toast-body. |
|||
//- Hello, world! This is a toast message. |
|||
//- ul |
|||
//- each item in flash.error |
|||
//- li #{item} |
|||
+script("js/common/flush.js") |
|||
- index = 0 |
|||
if flash.error |
|||
each item in flash.error |
|||
- index++ |
|||
.message.is-danger.animate__animated.animate__slideInRight(id="message"+index) |
|||
.message-header |
|||
p 错误 |
|||
button.delete.messagec(aria-label='delete' data-target="message"+index) |
|||
.message-body |
|||
| #{item} |
|||
if flash.success |
|||
each item in flash.success |
|||
- index++ |
|||
.message.is-success.animate__animated.animate__slideInRight(id="message"+index) |
|||
.message-header |
|||
p 成功 |
|||
button.delete.messagec(aria-label='delete' data-target="message"+index) |
|||
.message-body |
|||
| #{item} |
|||
if flash.info |
|||
each item in flash.info |
|||
- index++ |
|||
.message.is-info.animate__animated.animate__slideInRight(id="message"+index) |
|||
.message-header |
|||
p 信息 |
|||
button.delete.messagec(aria-label='delete' data-target="message"+index) |
|||
.message-body |
|||
| #{item} |
|||
if flash.warning |
|||
each item in flash.warning |
|||
- index++ |
|||
.message.is-warning.animate__animated.animate__slideInRight(id="message"+index) |
|||
.message-header |
|||
p 警告 |
|||
button.delete.messagec(aria-label='delete' data-target="message"+index) |
|||
.message-body |
|||
| #{item} |
|||
//- .toast-container.top-0.end-0.p-3 |
|||
//- each item in flash.error |
|||
//- .toast.show(role='alert', aria-live='assertive', aria-atomic='true') |
|||
//- .toast-header |
|||
//- img.rounded.me-2(src='/public/image/icons/error.svg', alt='错误' style="width:20px;height: 20px;") |
|||
//- strong.me-auto 提示 |
|||
//- //- small.text-muted just now |
|||
//- button.btn-close(type='button', data-bs-dismiss='toast', aria-label='Close') |
|||
//- .toast-body. |
|||
//- #{item} |
|||
//- .toast-container.position-fixed.bottom-0.end-0.p-3 |
|||
//- #liveToast.toast(role='alert', aria-live='assertive', aria-atomic='true') |
|||
//- .toast-header |
|||
//- img.rounded.me-2(src='...', alt='...') |
|||
//- strong.me-auto Bootstrap |
|||
//- small 11 mins ago |
|||
//- button.btn-close(type='button', data-bs-dismiss='toast', aria-label='Close') |
|||
//- .toast-body. |
|||
//- Hello, world! This is a toast message. |
|||
//- ul |
|||
//- each item in flash.error |
|||
//- li #{item} |
|||
+script("js/common/flush.js") |
|||
|
@ -1,10 +1,40 @@ |
|||
include @/helper/flush.pug |
|||
include @/helper/helper.pug |
|||
|
|||
title 首页 |
|||
block var |
|||
-title="首页" // 网页标题 |
|||
title 首页 |
|||
|
|||
div sad |
|||
if isLogin |
|||
button(hx-target="#single-page" hx-get="/about?htmx" hx-push-url="/about" hx-trigger="click" hx-swap="innerHTML") Click Me! |
|||
block page |
|||
div |
|||
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