[2024-07-14](INIT): 上传图片页面

This commit is contained in:
june 2024-07-14 20:31:27 +08:00
parent 64b776254e
commit a4c0ce45e9
13 changed files with 318 additions and 435 deletions

View File

@ -44,8 +44,8 @@ var picgoCmd = &cobra.Command{
// 创建路由
router.InitRouter()
// 启动HTTP服务器
log.Println("Serving on http://localhost:8082")
err := http.ListenAndServe(":8082", nil)
corelib.Logger.Infoln("Serving on http://", fmt.Sprintf("%s:%d", configs.Settings.Server.Host, configs.Settings.Server.Port))
err := http.ListenAndServe(fmt.Sprintf("%s:%d", configs.Settings.Server.Host, configs.Settings.Server.Port), nil)
if err != nil {
log.Fatalf("Failed to start server: %v", err)
}

View File

@ -1,53 +1,103 @@
package corelib
//import (
// "go.uber.org/zap"
// "go.uber.org/zap/zapcore"
//)
//var Logger *zap.SugaredLogger
//
//func NewLogger() {
// writeSyncer := getLogWriter()
// encoder := getEncoder()
// core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)
// logger := zap.New(core, zap.AddCaller())
// Logger = logger.Sugar()
//}
//
//func getEncoder() zapcore.Encoder {
// encoderConfig := zap.NewProductionEncoderConfig()
// encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
// encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
// return zapcore.NewConsoleEncoder(encoderConfig)
//}
//
//func getLogWriter() zapcore.WriteSyncer {
// var (
// err error
// currentDir string
// )
// // 获取当前工作目录
// if currentDir, err = os.Getwd(); err != nil {
// log.Panic("获取当前工作目录失败: ", err)
// }
// logPath := path.Join(currentDir, configs.Settings.Server.LogPath)
// // Filename: 日志文件的位置
// // MaxSize在进行切割之前日志文件的最大大小以 MB 为单位)
// // MaxBackups保留旧文件的最大个数
// // MaxAges保留旧文件的最大天数
// // Compress是否压缩 / 归档旧文件
// lumberJackLogger := &lumberjack.Logger{
// Filename: logPath,
// MaxSize: 50,
// MaxBackups: 10,
// MaxAge: 1,
// Compress: false,
// }
// return zapcore.AddSync(lumberJackLogger)
//}
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
"log"
"os"
"path"
"picgo/configs"
)
var Logger *zap.SugaredLogger
func NewLogger() {
writeSyncer := getLogWriter()
encoder := getEncoder()
core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)
logger := zap.New(core, zap.AddCaller())
config := zap.NewProductionConfig()
config.OutputPaths = []string{
"stdout",
"tmp/picgo-info.log",
}
config.ErrorOutputPaths = []string{
"stderr",
"tmp/picgo-error.log",
}
config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
logger, err := config.Build()
if err != nil {
panic(err)
}
Logger = logger.Sugar()
}
func getEncoder() zapcore.Encoder {
encoderConfig := zap.NewProductionEncoderConfig()
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
return zapcore.NewConsoleEncoder(encoderConfig)
func Panic(args ...interface{}) {
Logger.Panic(args...)
}
func Debug(args ...interface{}) {
Logger.Debug(args...)
}
func getLogWriter() zapcore.WriteSyncer {
var (
err error
currentDir string
)
// 获取当前工作目录
if currentDir, err = os.Getwd(); err != nil {
log.Panic("获取当前工作目录失败: ", err)
}
logPath := path.Join(currentDir, configs.Settings.Server.LogPath)
// Filename: 日志文件的位置
// MaxSize在进行切割之前日志文件的最大大小以 MB 为单位)
// MaxBackups保留旧文件的最大个数
// MaxAges保留旧文件的最大天数
// Compress是否压缩 / 归档旧文件
lumberJackLogger := &lumberjack.Logger{
Filename: logPath,
MaxSize: 50,
MaxBackups: 10,
MaxAge: 1,
Compress: false,
}
return zapcore.AddSync(lumberJackLogger)
func Info(args ...interface{}) {
Logger.Info(args...)
}
func Warn(args ...interface{}) {
Logger.Warn(args...)
}
func Error(args ...interface{}) {
Logger.Error(args...)
}
func Fatal(args ...interface{}) {
Logger.Fatal(args...)
}
func Sync() {
Logger.Sync()
}

View File

@ -1,15 +1,65 @@
package corelib
import (
"context"
"database/sql"
"go.uber.org/zap"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
gormLogger "gorm.io/gorm/logger"
"gorm.io/gorm/schema"
"picgo/configs"
"time"
)
type GormZapLogger struct {
logger *zap.SugaredLogger
gormLogger.Config
}
func (l *GormZapLogger) LogMode(level gormLogger.LogLevel) gormLogger.Interface {
newLogger := *l
newLogger.LogLevel = level
return &newLogger
}
func (l *GormZapLogger) Info(ctx context.Context, msg string, data ...interface{}) {
if l.LogLevel >= gormLogger.Info {
l.logger.Infof(msg, data...)
}
}
func (l *GormZapLogger) Warn(ctx context.Context, msg string, data ...interface{}) {
if l.LogLevel >= gormLogger.Warn {
l.logger.Warnf(msg, data...)
}
}
func (l *GormZapLogger) Error(ctx context.Context, msg string, data ...interface{}) {
if l.LogLevel >= gormLogger.Error {
l.logger.Errorf(msg, data...)
}
}
func (l *GormZapLogger) Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {
if l.LogLevel <= 0 {
return
}
elapsed := time.Since(begin)
switch {
case err != nil && l.LogLevel >= gormLogger.Error:
sql, rows := fc()
l.logger.Errorf("%s [%.2fms] [rows:%v] %s", err, float64(elapsed.Nanoseconds())/1e6, rows, sql)
case elapsed > l.SlowThreshold && l.SlowThreshold != 0 && l.LogLevel >= gormLogger.Warn:
sql, rows := fc()
l.logger.Warnf("SLOW SQL >= %v [%.2fms] [rows:%v] %s", l.SlowThreshold, float64(elapsed.Nanoseconds())/1e6, rows, sql)
case l.LogLevel >= gormLogger.Info:
sql, rows := fc()
l.logger.Infof("[%.2fms] [rows:%v] %s", float64(elapsed.Nanoseconds())/1e6, rows, sql)
}
}
var (
DbMysql *gorm.DB
)
@ -22,11 +72,23 @@ func NewMysql() {
sqlDB *sql.DB
dsn string
)
// 使用 zapgorm2 创建 GORM logger
dsn = configs.Settings.Mysql.MysqlDNS
zapLogger := Logger
newLogger := &GormZapLogger{
logger: zapLogger,
Config: gormLogger.Config{
SlowThreshold: time.Second,
LogLevel: gormLogger.Info,
Colorful: false,
},
}
if db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
// 配置日志级别打印出所有的sql
Logger: logger.Default.LogMode(logger.Info),
//Logger: logger.Default.LogMode(logger.Info),
Logger: newLogger,
NamingStrategy: schema.NamingStrategy{
//TablePrefix: configs.Setting.TablePrefix, // 表前缀
SingularTable: true, // 设置全局表名禁用复数

View File

@ -3,7 +3,7 @@
#------------------------
# 服务
server:
port: 8181
port: 8282
host: localhost
node_id: 1
log_path: tmp/picgo.log

View File

@ -1,19 +1,28 @@
package handler
import (
"github.com/gorilla/csrf"
"net/http"
"picgo/corelib"
)
func IndexHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
data := struct {
Title string
Active string
}{
Title: "Dashboard",
Active: r.URL.Path,
tmpData := map[string]interface{}{
csrf.TemplateTag: csrf.TemplateField(r),
}
corelib.TemplateHandler(w, data, "view/layout.html", "view/index.html")
tmpData["Title"] = "Dashboard"
tmpData["Active"] = r.URL.Path
//data := struct {
// Title string
// Active string
// //csrfField string
//}{
// Title: "Dashboard",
// Active: r.URL.Path,
// //csrfField: string(csrf.TemplateField(r)),
//}
w.Header().Add("X-CSRF-Token", csrf.Token(r))
corelib.TemplateHandler(w, tmpData, "view/layout.html", "view/index.html")
}
}

View File

@ -14,23 +14,10 @@ import (
func LoginHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
data := map[string]interface{}{
tmpData := map[string]interface{}{
csrf.TemplateTag: csrf.TemplateField(r),
}
//data := map[string]interface{}{csrf.TemplateTag: csrf.TemplateField(r)}
corelib.Logger.Info("data: ", data, data["csrfToken"])
corelib.TemplateHandler(w, data, "view/login.html")
//tmpl, err := template.ParseFiles("view/login.html")
//if err != nil {
// http.Error(w, "Internal Server Error", http.StatusInternalServerError)
// return
//}
//if err = tmpl.Execute(w, map[string]interface{}{
// csrf.TemplateTag: csrf.TemplateField(r),
//}); err != nil {
// http.Error(w, "Internal Server Error", http.StatusInternalServerError)
// return
//}
corelib.TemplateHandler(w, tmpData, "view/login.html")
case http.MethodPost:
loginService(w, r)
default:

View File

@ -1,19 +0,0 @@
.navbar-nav .nav-link {
font-size: 1.1em;
padding: 10px 20px;
}
.sidebar .nav-link {
font-size: 1.1em;
padding: 10px 15px;
}
.sidebar .nav-link:hover {
background-color: #f8f9fa;
color: #495057;
}
.sidebar .nav-link.active {
background-color: #e9ecef;
color: #007bff;
}

0
static/css/index.css Normal file
View File

View File

@ -52,3 +52,4 @@ Common.prototype.AlertSuccessToast = function (msg) {
var Alert = new Common()

View File

@ -1,338 +1,102 @@
function Domain() {
this.tableData = null
this.model = false
this.editIndex = -1
// this.url = "http://127.0.0.1:8300/domain"
this.url = "https://monitor.api.ggbba.top/domain"
this.currentPage = 1
this.pageData = null
this.searchText = ""
this.numPages = 0
}
function Index() {
// 搜索域名
Domain.prototype.searchDomain = function () {
let self = this
$('#search-btn').click(function () {
let search = $('#search-input').val()
self.searchText = search
if (self.searchText) {
self.currentPage = 1
self.initTableData(self.currentPage, self.searchText)
} else {
self.initTableData(1, "")
}
//阻止浏览器默认行。
Index.prototype.HoldbackBorwser = function () {
$(document).on({
dragleave: function (e) { //拖离
e.preventDefault();
},
drop: function (e) { //拖后放
e.preventDefault();
},
dragenter: function (e) { //拖进
e.preventDefault();
},
dragover: function (e) { //拖来拖去
e.preventDefault();
}
})
});
}
// 点击分页按钮
Domain.prototype.domainPageBtn = function () {
//监听拖拽事件
Index.prototype.MonitorDrop = function () {
let self = this
$('.page-btn').click(function () {
self.currentPage = parseInt($(this).attr('data-p'))
self.initTableData(self.currentPage, self.searchText)
})
}
// 分页初始化
Domain.prototype.domainPageInit = function () {
let self = this
// 是否是最后一页
let is_finally = false
// 是否是第一页
let is_first = false
// 总页数
let num_pages = self.pageData['num_pages']
if (self.currentPage === 1) {
is_first = true
}
if (self.currentPage === num_pages) {
is_finally = true
}
self.pageData['is_first'] = is_first
self.pageData['is_finally'] = is_finally
}
// 表单验证
Domain.prototype.verifyDomain = function (domain) {
let patt = /(https?|ftp|file):\/\/[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]/i
return patt.test(domain)
}
// 错误弹窗
Domain.prototype.alertError = function (msg) {
swal('提示', msg, 'error');
}
// 弹窗
Domain.prototype.alertToast = function (msg, type) {
swal({
'title': msg,
'text': '',
'type': type,
'showCancelButton': false,
'showConfirmButton': false,
'timer': 1000
})
}
// 带确认按钮的弹窗
Domain.prototype.alertConfirm = function (params) {
swal({
'title': params['title'] ? params['title'] : '提示',
'showCancelButton': true,
'showConfirmButton': true,
'type': params['type'] ? params['type'] : '',
'confirmButtonText': params['confirmText'] ? params['confirmText'] : '确定',
'cancelButtonText': params['cancelText'] ? params['cancelText'] : '取消',
'text': params['text'] ? params['text'] : ''
}, function (isConfirm) {
if (isConfirm) {
if (params['confirmCallback']) {
params['confirmCallback']()
}
} else {
if (params['cancelCallback']) {
params['cancelCallback']()
let box = document.getElementById('upload-btn-click'); //拖拽区域
box.addEventListener("drop", function (e) {
e.preventDefault(); //取消默认浏览器拖拽效果
let fileList = e.dataTransfer.files; //获取文件对象
//循环遍历文件对象
for (let i = 0; i < fileList.length; i++) {
//检测文件是不是图片
if (fileList[i].type.indexOf('image') === -1) {
Alert.AlertError("您拖的不是图片!")
return false;
}
//拖拉图片到浏览器,可以实现预览功能
// let img = window.URL.createObjectURL(fileList[i]);
// let filename = fileList[i].name; //图片名称
// let filesize = Math.floor((fileList[0].size) / 1024);
// if (filesize > 2048) {
// Alert.AlertError("上传大小不能超过2M.")
// return false;
// }
//上传
self.UploadFile(fileList[i]);
}
})
}, false);
}
// 成功弹窗
Domain.prototype.alertSuccessToast = function (msg) {
if (!msg) {
msg = '成功!'
}
this.alertToast(msg, 'success')
}
// 渲染模板
Domain.prototype.renderPage = function () {
let self = this;
let html = template('tpl-table-tr', {domainList: self.tableData})
$('#tbody-domin').empty().append(html)
html = template('tpl-page-li', {page_data: self.pageData})
$('#page-li').empty().append(html)
self.editDomain()
self.deleteDomain()
self.domainPageBtn()
}
// 初始化数据
Domain.prototype.initTableData = function (num, query) {
let self = this;
let requestUrl = self.url + '/page/' + num
if (query) {
requestUrl += "?search=" + query
}
$.get(requestUrl, function (data, status) {
if (data['code'] === 200) {
self.currentPage = num
self.tableData = data['data']['domain_data']
self.pageData = data['data']['pagination_data']
self.numPages = self.pageData['num_pages']
self.domainPageInit()
self.renderPage()
}
})
}
// 添加域名
Domain.prototype.addDomain = function () {
let self = this;
$("#add-domain").click(function () {
self.initAddForm()
})
}
// 初始化添加域名表单弹窗
Domain.prototype.initAddForm = function () {
this.model = false
$('#DomainModalLabel').text("添加域名")
$('#cdn-f').prop('checked', true)
$('#boce-f').prop('checked', true)
$('#form-key').val("")
$('#form-domain').val("")
$('#form-remark').val("")
}
// 提交添加域名表单
Domain.prototype.addSubmit = function () {
let self = this
let boceId = $("input[name='options-boce']:checked").attr("id")
let boce = false
if (boceId === "boce-t") {
boce = true
}
let cdnId = $("input[name='options-cdn']:checked").attr("id")
let cdn = false
if (cdnId === "cdn-t") {
cdn = true
}
let domain = $('#form-domain').val()
let autoKey = $('#form-key').val()
let remark = $('#form-remark').val()
let requestData = {'name': domain,'auth_key': auto_Key, 'remark': remark, 'boce': boce, 'category': cdn}
$("#DomainModal").modal("hide")
if (!self.verifyDomain(domain)) {
self.alertError("域名格式不正确")
return
//上传图片
Index.prototype.UploadFile = function (file) {
let filesize = Math.floor((file.size) / 1024);
if (filesize > 2048) {
Alert.AlertError("上传大小不能超过2M.")
return false;
}
let formData = new FormData();
formData.append('file', file);
let csrfToken = document.getElementsByName("gorilla.csrf.Token")[0].value
//开始上传图片
$.ajax({
type: "post",
url: self.url,
data: JSON.stringify(requestData),
contentType: "application/json; charset=utf-8",
dataType: "json",
url: '/upload',
type: 'POST',
data: formData,
contentType: false,
processData: false,
headers: {
"X-CSRF-Token": csrfToken
},
success: function (result) {
self.alertSuccessToast("添加成功!")
self.initTableData(self.numPages, '')
}
})
}
// 编辑域名
Domain.prototype.editDomain = function () {
let self = this;
$(".edit-domain").click(function () {
let index = $(this).attr('data-index')
self.initEditForm(index)
})
}
// 初始化编辑域名表单弹窗
Domain.prototype.initEditForm = function (index) {
this.editIndex = index
this.model = true
$('#DomainModalLabel').text("编辑域名")
let data = this.tableData[index]
if (data['category']) {
$('#cdn-t').prop('checked', true)
} else {
$('#cdn-f').prop('checked', true)
}
if (data['boce']) {
$('#boce-t').prop('checked', true)
} else {
$('#boce-f').prop('checked', true)
}
$('#form-key').val(data['auth_key'])
$('#form-domain').val(data['name'])
$('#form-remark').val(data['remark'])
}
// 编辑域名表单提交
Domain.prototype.editSubmit = function () {
let self = this
if (self.model) {
let data = this.tableData[self.editIndex]
let id = data['id']
let boceId = $("input[name='options-boce']:checked").attr("id")
let boce = false
if (boceId === "boce-t") {
boce = true
}
let cdnId = $("input[name='options-cdn']:checked").attr("id")
let cdn = false
if (cdnId === "cdn-t") {
cdn = true
}
let domain = $('#form-domain').val()
let autoKey = $('#form-key').val()
let remark = $('#form-remark').val()
let requestData = {'id': id, 'name': domain, 'auth_key': autoKey, 'remark': remark, 'boce': boce, 'category': cdn}
$("#DomainModal").modal("hide")
if (!self.verifyDomain(domain)) {
self.alertError("域名格式不正确")
return
}
$.ajax({
type: "put",
url: self.url,
data: JSON.stringify(requestData),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (result) {
self.alertSuccessToast("编辑成功!")
self.tableData[self.editIndex] = requestData
self.renderPage()
}
})
}
}
// 表单提交策略
Domain.prototype.submitData = function () {
let self = this;
$('#submit-data').click(function () {
if (self.model) {
self.editSubmit()
} else {
self.addSubmit()
}
})
}
// 删除域名
Domain.prototype.deleteDomain = function () {
let self = this;
$('.delete-domain').click(function () {
let index = $(this).attr('data-index')
let requestData = {'id': self.tableData[index]['id']}
self.alertConfirm({
'text': '您确定要删除' + self.tableData[index]['name'] + '吗?',
'confirmCallback': function () {
$.ajax({
type: "delete",
url: self.url,
data: JSON.stringify(requestData),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (result) {
self.alertSuccessToast("删除成功!")
self.initTableData(self.currentPage, self.searchText)
}
})
}
})
})
}
// 入口方法
Domain.prototype.run = function () {
let self = this
self.initTableData(self.currentPage, '')
self.addDomain()
self.editDomain()
self.submitData()
self.deleteDomain()
self.domainPageBtn()
self.searchDomain()
}
// 构造执行入口
$(function () {
// 模板过滤方法
if (window.template) {
template.defaults.imports.domainSubstring = function (dateValue) {
if (dateValue.length > 40 ) {
return dateValue.substring(0, 37) + "..."
if (result.status === 200) {
Alert.AlertSuccessToast("上传成功!")
// alert("上传成功!文件名为:" + result.data);
} else {
return dateValue
Alert.AlertError(result.message)
return false;
}
},
error: function () {
Alert.AlertError("服务器内部错误")
}
}
let domain = new Domain()
domain.run()
})
})
}
Index.prototype.UploadClick = function () {
$("#upload-btn-click").click(function () {
$("#upload-input-file").click()
})
}
Index.prototype.run = function () {
this.HoldbackBorwser()
this.MonitorDrop()
this.UploadClick()
}
$(function () {
let index = new Index()
index.run()
})

View File

@ -17,7 +17,8 @@ Login.prototype.RefreshCaptcha = function () {
Login.prototype.LoginClick = function () {
let self = this;
let csrfToken = document.getElementsByName("gorilla.csrf.Token")[0].value
$("#login-btn").click(function () {
$("#login-btn").click(function (e) {
e.preventDefault();
// 获取参数
let username = $("#login-username").val()
let password = $("#login-password").val()
@ -36,11 +37,11 @@ Login.prototype.LoginClick = function () {
"X-CSRF-Token": csrfToken
},
success: function (result) {
if (result["status"] === 200) {
Alert.AlertSuccessToast(result["message"])
if (result.status === 200) {
Alert.AlertSuccessToast(result.message)
window.location.href = "/"
} else {
Alert.AlertError(result["message"])
Alert.AlertError(result.message)
}
},
error: function (xhr, status, error) {

View File

@ -1,23 +1,49 @@
{{define "style"}}
<link href="/static/css/index.css" rel="stylesheet">
{{end}}
{{define "content"}}
<div style="background-color: #f2f4f7">
<div class="upload-zone"
style="height: 248px; background-color: rgb(255, 255, 255); border-radius: 6px; padding: 24px;">
<span class="ant-upload-wrapper" style="box-sizing: border-box; margin: 0; padding: 0; color: rgba(0, 0, 0, 0.88);font-size: 14px;line-height: 1.5714285714285714;list-style: none;">
<div class="css-3rel02 ant-upload ant-upload-drag">
<span tabindex="0" class="ant-upload ant-upload-btn" role="button">
<input type="file" accept="" multiple="" style="display: none;">
<div class="ant-upload-drag-container" style="display: table-cell;vertical-align: middle;">
<p class="ant-upload-drag-icon">
<img src="/static/img/add.svg">
</p>
<p class="ant-upload-text" style="font-size: 14px; margin: 0px;">拖拽文件到此区域或
<span style="color: rgb(26, 102, 255);">点此上传</span>
</p>
<p class="ant-upload-hint" style="font-size: 12px; margin: 4px 0px 0px;">支持上传照片、视频、ZIP、pdf等多种文件最大支持100M上传后支持复制url、base64、Markdown 照片三种方式</p>
</div>
</span>
<div class="card text-center">
<div class="card-header" style="background-color: #fcf8e3">
<h4>图片上传</h4>
</div>
<div class="card-body" style="border-style:dashed; border-color:#98bf21; display: block">
<input type="file" class="form-control-file" id="upload-input-file" style="display:none;">
<div id="upload-btn-click">
<div style="height: 100px"></div>
<div class="ant-upload-drag-container">
<p class="ant-upload-drag-icon"><img src="/static/img/add.svg"></p>
<p class="ant-upload-text" style="font-size: 14px; margin: 0;">拖拽文件到此区域或<span
style="color: rgb(26, 102, 255);">点此上传</span></p>
<p class="ant-upload-hint" style="font-size: 12px; margin: 4px 0 0;">
只支持上传图片文件最大支持2M上传后支持复制url</p>
</div>
</span>
<div style="height: 100px"></div>
</div>
</div>
<div class="card-footer text-muted" style="background-color: #fcf8e3">
<div id="preview"></div>
<div class="input-group mb-3">
<input type="text" class="form-control" placeholder="图片链接" aria-label="图片链接"
aria-describedby="button-addon2">
<div class="input-group-append">
<button class="btn btn-outline-primary" type="button" id="button-addon2">复制链接</button>
</div>
</div>
</div>
</div>
{{end}}
{{define "script"}}
{{/*
<script id="tpl-image" type="text/html">
<div class="card" style="width: 18rem;">
<img src="{{ url }}" class="card-img-top" alt="...">
<div class="card-body">
<button type="button" class="btn btn-primary">点击复制按钮</button>
</div>
</div>
</script> */}}
<script src="/static/js/index.js"></script>
{{end}}

View File

@ -5,7 +5,14 @@
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="shortcut icon" href="/static/img/favicon.ico">
<link href="/static/css/bootstrap.min.css" rel="stylesheet">
<link href="/static/css/custom.css" rel="stylesheet">
<link href="/static/css/sweetalert.css" rel="stylesheet">
<script src="/static/js/jquery-3.3.1.min.js"></script>
<script src="/static/js/popper.min.js"></script>
<script src="/static/js/bootstrap.min.js"></script>
<!-- <script src="/static/js/feather-icons.js"></script>-->
<script src="/static/js/sweetalert.min.js"></script>
<script src="/static/js/common.js"></script>
{{template "style" .}}
<title>{{.Title}}</title>
</head>
<body>
@ -37,14 +44,9 @@
</div>
<div class="container">
{{ .csrfField }}
{{template "content" .}}
</div>
<script src="/static/js/jquery-3.3.1.min.js"></script>
<script src="/static/js/bootstrap.min.js"></script>
<!--<script src="/static/js/feather-icons.js"></script>-->
<!--<script>-->
<!-- feather.replace()-->
<!--</script>-->
{{template "script" .}}
</body>
</html>