npmrun 2 years ago
parent
commit
201028f527
  1. BIN
      data/data.db
  2. 1
      docs/a.md
  3. 8
      docs/index.md
  4. 2
      public/js/common/flush.js
  5. 11
      public/style/common/style.css
  6. 124
      public/style/views/index.css
  7. 26
      public/style/views/test.css
  8. 26
      route.txt
  9. 5
      source/models/color.ts
  10. 56
      source/models/color_cate.ts
  11. 1
      source/plugins/index.ts
  12. 4
      source/plugins/router-plugin/index.ts
  13. 24
      source/route/views/index.ts
  14. 7
      source/run.ts
  15. 1
      template/404.pug
  16. 121
      template/helper/flush.pug
  17. 38
      template/htmx/path/index.pug
  18. 2
      template/layout/layout.pug
  19. 1
      template/views/login.pug
  20. 1
      template/views/register.pug
  21. 3
      template/views/resume.pug
  22. 31
      template/views/test.pug
  23. 2
      types/global.d.ts

BIN
data/data.db

Binary file not shown.

1
docs/a.md

@ -1 +0,0 @@
## Hello World

8
docs/index.md

@ -0,0 +1,8 @@
# 欢迎做客前端作坊
> 作者:YSir
> 站点:[个人单页](https://xieyaxin.top)
## 前言
又是一个新项目,工作这些年,也不知做了多少项目,大多数是难产而亡,本项目是第一个比较完整的后端项目,开发此站的目的仅是学习学习hapijs,最后写着写着就不想再搞其他的框架了,折腾累了,对于繁杂的网络信息,我感到神经有些疲劳,因此就先开发着这个项目,把完整的开源项目流程走一遍。

2
public/js/common/flush.js

@ -1,4 +1,4 @@
var $messages = Array.prototype.slice.call(document.querySelectorAll(".message-container .message button.delete"), 0)
var $messages = Array.prototype.slice.call(document.querySelectorAll(".message button.delete"), 0)
$messages.forEach((el, index) => {
let timeID
function click() {

11
public/style/common/style.css

@ -2,7 +2,7 @@ html {
overflow-y: auto;
}
.message-container {
/* .message-container {
position: fixed;
right: 0;
top: 0;
@ -12,8 +12,13 @@ html {
}
.message-container::-webkit-scrollbar {
display: none;
}
.message-container .message {
} */
.message {
position: fixed;
right: 0;
top: 0;
z-index: 999;
overflow: auto;
min-width: 250px;
max-width: 250px;
margin: 25px;

124
public/style/views/index.css

@ -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");
}

26
public/style/views/test.css

@ -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;
}

26
route.txt

@ -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

5
source/models/color.ts

@ -47,6 +47,9 @@ export default function ColorModel(sequelize: Sequelize) {
delete values.deletedAt
return values
}
Color.associate = function (models) { }
Color.associate = function (models) {
models["color"].hasMany(models["color_cate"])
models["color_cate"].belongsTo(models["color"])
}
return Color
}

56
source/models/color_cate.ts

@ -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
source/plugins/index.ts

@ -117,7 +117,6 @@ export default [
}
}, {})
}
console.log(temp)
request.yar.set("_flash", {})
// @ts-ignore
request.response.source.context = Hoek.applyToDefaults(

4
source/plugins/router-plugin/index.ts

@ -161,9 +161,9 @@ class routePlugin {
) {
str = " 需要权限 : " + " " + full(method) + " " + str
} else if (typeof options.auth === "object" && options.auth.mode === "optional") {
str = " 不需权限(提供即需验证): " + " " + full(method) + " " + str
str = " 不需权限(提供即需验证optional): " + " " + full(method) + " " + str
} else if (typeof options.auth === "object" && options.auth.mode === "try") {
str = " 不需权限(提供无需验证): " + " " + full(method) + " " + str
str = " 不需权限(提供无需验证try): " + " " + full(method) + " " + str
} else {
str = " 不需权限 : " + " " + full(method) + " " + str
}

24
source/route/views/index.ts

@ -6,13 +6,17 @@ import { baseDir, gFail, sourceDir } from "@/util"
import MarkdownIt from "markdown-it"
export default class Index {
@auth("try")
@auth("optional")
async index(request: Req, h: Res): ReturnValue {
const md = new MarkdownIt()
var result = md.render(fs.readFileSync(path.resolve(baseDir, "./docs/index.md"), "utf-8"))
const isRenderHtmx = Reflect.has(request.query, "htmx")
if (isRenderHtmx) {
return h.view("htmx/path/index.pug", { isLogin: request.auth.isAuthenticated })
return h.view("htmx/path/index.pug", { isLogin: request.auth.isAuthenticated, md: result })
}
return h.view("views/index.pug", { isLogin: request.auth.isAuthenticated })
return h.view("views/index.pug", { isLogin: request.auth.isAuthenticated, md: result })
}
@route("/about")
@ -86,7 +90,19 @@ export default class Index {
@route("/color")
async color_get(request: Req, h: Res) {
const ColorModel = request.getModel("color")
const colorList = await ColorModel.findAll()
const colorList = await ColorModel.findAll({})
// const colorList = await ColorModel.findAll({
// include: [
// { model: request.getModel("color_cate"), attributes: ['name'] }
// ]
// })
// console.log(colorList);
// const ColorCateModel = request.getModel("color_cate")
// console.log(await ColorCateModel.findAll({
// include: [request.getModel("color")]
// }))
const isRenderHtmx = Reflect.has(request.query, "htmx")
if (isRenderHtmx) {
return h.view(`htmx/path/color.pug`, { list: colorList })

7
source/run.ts

@ -24,9 +24,9 @@ const run = async (): Promise<Server> => {
})
server.events.on('request', (request, event, tags) => {
if (tags.error) {
loggerSite.error(event);
loggerSite.error(request.path, "\n", event);
} else {
loggerSite.info(event);
loggerSite.info(request.path, "\n", event);
}
});
server.events.on('log', (event, tags) => {
@ -70,6 +70,9 @@ const run = async (): Promise<Server> => {
name: "sid", //cookie的名字
password: process.env.KEY,
isSecure: false, // false: 允许 Cookie 通过不安全的连接传输,这会使其受到攻击
isHttpOnly: true,
clearInvalid: false,
strictHeader: true
},
redirectTo(request: Req) {
if (request.path.startsWith("/api")) {

1
template/404.pug

@ -1,4 +1,5 @@
extends layout/layout
include @/helper/flush.pug
block head
link(rel="stylesheet", href="/public/css/views/404.css")

121
template/helper/flush.pug

@ -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")

38
template/htmx/path/index.pug

@ -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="") 新建

2
template/layout/layout.pug

@ -1,6 +1,5 @@
doctype html
include @/helper/helper.pug
//- include @/helper/flush.pug
block var
html(lang="zh-cn" class=hideHeader?"":"has-navbar-fixed-top")
@ -18,7 +17,6 @@ html(lang="zh-cn" class=hideHeader?"":"has-navbar-fixed-top")
script!= "window.STATIC_USER = "+JSON.stringify(user)
//- window.STATIC_DATA = #{JSON.stringify(user)}
body
//- include @/helper/flush.pug
if !hideHeader
include @/ui/header.pug
block content

1
template/views/login.pug

@ -8,6 +8,7 @@ block head
+css("style/views/login.css")
block content
include @/helper/flush.pug
.login
h1.title.is-1 登录
form(action='/login' method='post')

1
template/views/register.pug

@ -1,4 +1,5 @@
extends @/layout/layout
include @/helper/flush.pug
block var
-title="注册" // 网页标题

3
template/views/resume.pug

@ -1,3 +1,6 @@
include @/helper/helper.pug
include @/helper/flush.pug
doctype html
html(lang='zh')
head

31
template/views/test.pug

@ -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 })
})

2
types/global.d.ts

@ -3,6 +3,7 @@ import { Server } from "@hapi/hapi"
import { Request, ResponseToolkit, Lifecycle } from "@hapi/hapi"
import { TUserModel } from "@/models/user"
import { TColorModel } from "@/models/color"
import { TColorCateModel } from "@/models/color_cate"
import yar from "@hapi/yar"
import { TAttachmentModel } from "@/models/Attachment"
@ -16,6 +17,7 @@ declare global {
interface Models {
user: TUserModel
color: TColorModel
color_cate: TColorCateModel
attachment: TAttachmentModel
}

Loading…
Cancel
Save