博客 (145)

该问题将在 MySQL Connector/NET 8.0.24 中修复,参考:https://bugs.mysql.com/bug.php?id=102381

xoyozo 4 年前
2,891

如果  http 与 https 使用相同的端口号会导致 IIS Express 无法启动

image.png

更换不同端口号即可:

image.png

在项目文件右键属性,“调试”选项卡中配置。

Tip:修改完以后要保存,否则不会生效。

另外,如果要允许 http 访问,那么 Startup.cs 的 Configure 方法中就不能 app.UseHttpsRedirection();

xoyozo 4 年前
4,287

.menu-nav 包裹

<ul class="menu-nav">
...
</ul>


.menu-section 区段

<li class="menu-section">
    <h4 class="menu-text">区段</h4>
    <i class="menu-icon ki ki-bold-more-hor icon-md"></i>
</li>


.menu-item 菜单

<li class="menu-item" aria-haspopup="true">
    <a href="###" class="menu-link">
        <span class="svg-icon menu-icon">
            <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24px" height="24px" viewBox="0 0 24 24" version="1.1">
                <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
                    <rect x="0" y="0" width="24" height="24" />
                    <path d="M3,16 L5,16 C5.55228475,16 6,15.5522847 6,15 C6,14.4477153 5.55228475,14 5,14 L3,14 L3,12 L5,12 C5.55228475,12 6,11.5522847 6,11 C6,10.4477153 5.55228475,10 5,10 L3,10 L3,8 L5,8 C5.55228475,8 6,7.55228475 6,7 C6,6.44771525 5.55228475,6 5,6 L3,6 L3,4 C3,3.44771525 3.44771525,3 4,3 L10,3 C10.5522847,3 11,3.44771525 11,4 L11,19 C11,19.5522847 10.5522847,20 10,20 L4,20 C3.44771525,20 3,19.5522847 3,19 L3,16 Z" fill="#000000" opacity="0.3" />
                    <path d="M16,3 L19,3 C20.1045695,3 21,3.8954305 21,5 L21,15.2485298 C21,15.7329761 20.8241635,16.200956 20.5051534,16.565539 L17.8762883,19.5699562 C17.6944473,19.7777745 17.378566,19.7988332 17.1707477,19.6169922 C17.1540423,19.602375 17.1383289,19.5866616 17.1237117,19.5699562 L14.4948466,16.565539 C14.1758365,16.200956 14,15.7329761 14,15.2485298 L14,5 C14,3.8954305 14.8954305,3 16,3 Z" fill="#000000" />
                </g>
            </svg>
        </span>
        <span class="menu-text">一级菜单(无子菜单)</span>
    </a>
</li>

其中,<svg /> 可以替换成其它图标库,如:

<li class="menu-item" aria-haspopup="true">
    <a href="###" class="menu-link">
        <span class="svg-icon menu-icon">
            <i class="fab fa-app-store-ios"></i>
        </span>
        <span class="menu-text">一级菜单(无子菜单)</span>
    </a>
</li>

并修正样式:

.aside-menu .menu-nav > .menu-item > .menu-heading .menu-icon i,
.aside-menu .menu-nav > .menu-item > .menu-link .menu-icon i { height: 23px; width: 23px; line-height: 23px; text-align: center; vertical-align: middle; font-size: 1.25rem; }


.menu-item-submenu 表示该 .menu-item 有子菜单,即内部包含 .menu-submenu。该规则在任意一级菜单均有效。

.menu-item-open 表示该 .menu-item 当前是打开状态(高亮),否则为闭合状态,需配合 <i class="menu-arrow"></i> 使用。该规则在任意一级菜单均有效。

.menu-item-active 表示该 .menu-item 对应当前页面,效果为灰色背景且文字持续高亮。

.menu-item-parent 作为一级菜单在不同设备上展示的替代,位于一级菜单下的 .menu-submenu 下的 .menu-subnav 内部。

.menu-link 表示可点击(hover 效果),用于 .menu-item 下的 <a /> 或 .menu-item-parent 下的 <span />。

aria-haspopup="true" 每个 .menu-item 都有。

data-menu-toggle="hover" 每个 .menu-item-submenu 都有。

.menu-icon 一级菜单项的左侧图标,支持 <svg /> 或 <i />。

.menu-bullet 非一级菜单项的左侧图标,与 .menu-text 同级,可选 .menu-bullet-line / .menu-bullet-dot。

.menu-text 菜单文字。

.menu-label 菜单项右侧徽章,与 .menu-text 同级。

.menu-arrow 菜单项右侧箭头(当包含子菜单时使用),与 .menu-text 同级。

.menu-toggle 放在 .menu-item-submenu 内部的 .menu-link,与 .menu-item-submenu 上的  data-menu-toggle="hover" 同时出现。


完整示例:

<ul class="menu-nav">
    <li class="menu-item" aria-haspopup="true">
        <a href="index.html" class="menu-link">
            <span class="svg-icon menu-icon">
                <!--begin::Svg Icon | path:assets/media/svg/icons/Design/Layers.svg-->
                <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24px" height="24px" viewBox="0 0 24 24" version="1.1">
                    <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
                        <polygon points="0 0 24 0 24 24 0 24" />
                        <path d="M12.9336061,16.072447 L19.36,10.9564761 L19.5181585,10.8312381 C20.1676248,10.3169571 20.2772143,9.3735535 19.7629333,8.72408713 C19.6917232,8.63415859 19.6104327,8.55269514 19.5206557,8.48129411 L12.9336854,3.24257445 C12.3871201,2.80788259 11.6128799,2.80788259 11.0663146,3.24257445 L4.47482784,8.48488609 C3.82645598,9.00054628 3.71887192,9.94418071 4.23453211,10.5925526 C4.30500305,10.6811601 4.38527899,10.7615046 4.47382636,10.8320511 L4.63,10.9564761 L11.0659024,16.0730648 C11.6126744,16.5077525 12.3871218,16.5074963 12.9336061,16.072447 Z" fill="#000000" fill-rule="nonzero" />
                        <path d="M11.0563554,18.6706981 L5.33593024,14.122919 C4.94553994,13.8125559 4.37746707,13.8774308 4.06710397,14.2678211 C4.06471678,14.2708238 4.06234874,14.2738418 4.06,14.2768747 L4.06,14.2768747 C3.75257288,14.6738539 3.82516916,15.244888 4.22214834,15.5523151 C4.22358765,15.5534297 4.2250303,15.55454 4.22647627,15.555646 L11.0872776,20.8031356 C11.6250734,21.2144692 12.371757,21.2145375 12.909628,20.8033023 L19.7677785,15.559828 C20.1693192,15.2528257 20.2459576,14.6784381 19.9389553,14.2768974 C19.9376429,14.2751809 19.9363245,14.2734691 19.935,14.2717619 L19.935,14.2717619 C19.6266937,13.8743807 19.0546209,13.8021712 18.6572397,14.1104775 C18.654352,14.112718 18.6514778,14.1149757 18.6486172,14.1172508 L12.9235044,18.6705218 C12.377022,19.1051477 11.6029199,19.1052208 11.0563554,18.6706981 Z" fill="#000000" opacity="0.3" />
                    </g>
                </svg>
                <!--end::Svg Icon-->
            </span>
            <span class="menu-text">Dashboard</span>
        </a>
    </li>
    <li class="menu-section">
        <h4 class="menu-text">区段 A(一级菜单示例)</h4>
        <i class="menu-icon ki ki-bold-more-hor icon-md"></i>
    </li>
    <li class="menu-item" aria-haspopup="true">
        <a href="item1" class="menu-link">
            <span class="svg-icon menu-icon">
                <i class="fas fa-dice-one"></i>
            </span>
            <span class="menu-text">菜单 A</span>
        </a>
    </li>
    <li class="menu-item" aria-haspopup="true">
        <a href="item2" class="menu-link">
            <span class="svg-icon menu-icon">
                <i class="fas fa-dice-two"></i>
            </span>
            <span class="menu-text">菜单 B(带徽章)</span>
            <span class="menu-label">
                <span class="label label-rounded label-primary">6</span>
            </span>
        </a>
    </li>
    <li class="menu-item menu-item-active" aria-haspopup="true">
        <a href="item2" class="menu-link">
            <span class="svg-icon menu-icon">
                <i class="fas fa-dice-three"></i>
            </span>
            <span class="menu-text">菜单 C(高亮)</span>
        </a>
    </li>
    <li class="menu-section">
        <h4 class="menu-text">区段 B(二级菜单示例)</h4>
        <i class="menu-icon ki ki-bold-more-hor icon-md"></i>
    </li>
    <li class="menu-item menu-item-submenu" aria-haspopup="true" data-menu-toggle="hover">
        <a href="javascript:;" class="menu-link menu-toggle">
            <span class="svg-icon menu-icon">
                <i class="fas fa-dice-one"></i>
            </span>
            <span class="menu-text">一级菜单(默认关闭)</span>
            <i class="menu-arrow"></i>
        </a>
        <div class="menu-submenu">
            <i class="menu-arrow"></i>
            <ul class="menu-subnav">
                <li class="menu-item menu-item-parent" aria-haspopup="true">
                    <span class="menu-link">
                        <span class="menu-text">一级菜单(默认关闭)</span>
                    </span>
                </li>
                <li class="menu-item" aria-haspopup="true">
                    <a href="custom/apps/inbox.html" class="menu-link">
                        <i class="menu-bullet menu-bullet-line"><span></span></i>
                        <span class="menu-text">二级菜单 a</span>
                    </a>
                </li>
                <li class="menu-item" aria-haspopup="true">
                    <a href="custom/apps/inbox.html" class="menu-link">
                        <i class="menu-bullet menu-bullet-line"><span></span></i>
                        <span class="menu-text">二级菜单 b(带徽章)</span>
                        <span class="menu-label">
                            <span class="label label-danger label-inline">new</span>
                        </span>
                    </a>
                </li>
            </ul>
        </div>
    </li>
    <li class="menu-item menu-item-submenu menu-item-here menu-item-open" aria-haspopup="true" data-menu-toggle="hover">
        <a href="javascript:;" class="menu-link menu-toggle">
            <span class="svg-icon menu-icon">
                <i class="fas fa-dice-two"></i>
            </span>
            <span class="menu-text">一级菜单(高亮/默认打开)</span>
            <i class="menu-arrow"></i>
        </a>
        <div class="menu-submenu">
            <i class="menu-arrow"></i>
            <ul class="menu-subnav">
                <li class="menu-item menu-item-parent" aria-haspopup="true">
                    <span class="menu-link">
                        <span class="menu-text">一级菜单(高亮/默认打开)</span>
                    </span>
                </li>
                <li class="menu-item menu-item-active" aria-haspopup="true">
                    <a href="custom/apps/inbox.html" class="menu-link">
                        <i class="menu-bullet menu-bullet-line"><span></span></i>
                        <span class="menu-text">二级菜单 c(高亮)</span>
                    </a>
                </li>
                <li class="menu-item" aria-haspopup="true">
                    <a href="custom/apps/inbox.html" class="menu-link">
                        <i class="menu-bullet menu-bullet-line"><span></span></i>
                        <span class="menu-text">二级菜单 d(带徽章)</span>
                        <span class="menu-label">
                            <span class="label label-danger label-inline">new</span>
                        </span>
                    </a>
                </li>
            </ul>
        </div>
    </li>
    <li class="menu-section">
        <h4 class="menu-text">区段 C(三级菜单示例)</h4>
        <i class="menu-icon ki ki-bold-more-hor icon-md"></i>
    </li>
    <li class="menu-item menu-item-submenu menu-item-open menu-item-here" aria-haspopup="true" data-menu-toggle="hover">
        <a href="javascript:;" class="menu-link menu-toggle">
            <span class="svg-icon menu-icon">
                <i class="fas fa-dice-one"></i>
            </span>
            <span class="menu-text">一级菜单</span>
            <i class="menu-arrow"></i>
        </a>
        <div class="menu-submenu">
            <i class="menu-arrow"></i>
            <ul class="menu-subnav">
                <li class="menu-item menu-item-parent" aria-haspopup="true">
                    <span class="menu-link">
                        <span class="menu-text">一级菜单</span>
                    </span>
                </li>
                <li class="menu-item menu-item-submenu menu-item-open menu-item-here" aria-haspopup="true" data-menu-toggle="hover">
                    <a href="javascript:;" class="menu-link menu-toggle">
                        <i class="menu-bullet menu-bullet-line"><span></span></i>
                        <span class="menu-text">二级菜单(默认打开)</span>
                        <span class="menu-label">
                            <span class="label label-rounded label-primary">6</span>
                        </span>
                        <i class="menu-arrow"></i>
                    </a>
                    <div class="menu-submenu">
                        <i class="menu-arrow"></i>
                        <ul class="menu-subnav">
                            <li class="menu-item" aria-haspopup="true">
                                <a href="custom/apps/user/list-default.html" class="menu-link">
                                    <i class="menu-bullet menu-bullet-dot"><span></span></i>
                                    <span class="menu-text">三级菜单 1</span>
                                </a>
                            </li>
                            <li class="menu-item menu-item-active" aria-haspopup="true">
                                <a href="custom/apps/user/list-datatable.html" class="menu-link">
                                    <i class="menu-bullet menu-bullet-dot"><span></span></i>
                                    <span class="menu-text">三级菜单 2(高亮)</span>
                                </a>
                            </li>
                        </ul>
                    </div>
                </li>
                <li class="menu-item menu-item-submenu" aria-haspopup="true" data-menu-toggle="hover">
                    <a href="javascript:;" class="menu-link menu-toggle">
                        <i class="menu-bullet menu-bullet-line"><span></span></i>
                        <span class="menu-text">二级菜单(默认关闭)</span>
                        <i class="menu-arrow"></i>
                    </a>
                    <div class="menu-submenu">
                        <i class="menu-arrow"></i>
                        <ul class="menu-subnav">
                            <li class="menu-item menu-item-submenu" aria-haspopup="true" data-menu-toggle="hover">
                                <a href="javascript:;" class="menu-link menu-toggle">
                                    <i class="menu-bullet menu-bullet-dot"><span></span></i>
                                    <span class="menu-text">二级菜单(默认关闭)</span>
                                    <i class="menu-arrow"></i>
                                </a>
                                <div class="menu-submenu">
                                    <i class="menu-arrow"></i>
                                    <ul class="menu-subnav">
                                        <li class="menu-item" aria-haspopup="true">
                                            <a href="custom/apps/profile/profile-1/overview.html" class="menu-link">
                                                <i class="menu-bullet menu-bullet-line"><span></span></i>
                                                <span class="menu-text">三级菜单 3</span>
                                            </a>
                                        </li>
                                    </ul>
                                </div>
                            </li>
                        </ul>
                    </div>
                </li>
            </ul>
        </div>
    </li>
</ul>

效果:

image.png

关于手风琴功能:Metronic 菜单已实现手风琴功能,即当展开一个闭合的菜单时,非当前页面所在菜单项的房系菜单会自动闭合。

xoyozo 5 年前
5,762

报错:

HTTP Error 500.30 - ASP.NET Core app failed to start

Common solutions to this issue:

  • The app failed to start

  • The app started but then stopped

  • The app started but threw an exception during startup

Troubleshooting steps:

  • Check the system event log for error messages

  • Enable logging the application process' stdout messages

  • Attach a debugger to the application process and inspect

For more information visit: https://go.microsoft.com/fwlink/?LinkID=2028265


解决:

直接运行网站根目录的 .exe 启动文件可以找到答案。


一个常见的原因是:因 nuget 包升级(特别是需要服务器端安装组件的包)导致的发布后仍然是低版本的包,删除项目中的 /obj/ 目录重新发布即可。

xoyozo 5 年前
8,129

纯真 IP 数据库官方下载地址:http://www.cz88.net/

下载安装纯真 IP,将 qqwry.dat 文件复制到项目下。(本文以放在 /App_Data/ 目录下为例)

在 qqwry.dat 上右键打开属性窗口,将“复制到输出目录”切换到“始终复制”或“如果较新则复制”。

通过 nuget 安装 QQWry,喜欢依赖注入方式的可以选择 QQWry.DependencyInjectio。

var config = new QQWryOptions
{
    DbPath = dbPath ?? (AppContext.BaseDirectory + @"App_Data\qqwry.dat")
};

var ipSearch = new QQWryIpSearch(config);

var ipl = ipSearch.GetIpLocation(ip);

return new IpLocation
{
    Ip = ipl.Ip,
    Country = ipl.Country?.Replace("CZ88.NET", "").Trim(),
    Area = ipl.Area?.Replace("CZ88.NET", "").Trim(),
};

github 开源地址:https://github.com/JadynWong/IP_qqwry


xoyozo 5 年前
3,835

核心文件路径:/theme/html/demo*/src/js/components/core.datatable.js

所有参数的默认值见该文件 3369 行起。

data:

属性 功能
type 数据源类型 local / remote
source 数据源 链接或对象(见下方)
pageSize 每页项数 默认 10
saveState 刷新、重新打开、返回时仍保持状态 默认 true
serverPaging 是否在服务端实现分页 默认 false
serverFiltering 是否在服务端实现筛选 默认 false
serverSorting 是否在服务端实现排序 默认 false
autoColumns 为远程数据源启用自动列功能 默认 false
attr

data.source:

属性 功能
url 数据源地址
params 请求参数
query: {
}
headers 自定义请求的头
{
    'x-my-custom-header': 'some value',
    'x-test-header': 'the value'
}
map 数据地图,作用是对返回的数据进行整理和定位
function (raw) {
    console.log(raw)
    // sample data mapping
    var dataSet = raw;
    if (typeof raw.data !== 'undefined') {
        dataSet = raw.data;
    }
    return dataSet;
}

layout:

属性 功能
theme 主题 默认 default
class 包裹的 CSS 样式
scroll 在需要时显示横向或纵向滚动条 默认 false
height 表格高度 默认 null
minHeight 表格最小高度 默认 null
footer 是否显示表格底部 默认 false
header 是否显示表头 默认 true
customScrollbar 自定义的滚动条 默认 true
spinner Loading 样式
{
	overlayColor: '#000000',
	opacity: 0,
	type: 'loader',
	state: 'primary',
	message: true,
}
icons 表格中的 icon
{
	sort: {
	    asc: 'flaticon2-arrow-up', 
	    desc: 'flaticon2-arrow-down'
	},
	pagination: {
		next: 'flaticon2-next',
		prev: 'flaticon2-back',
		first: 'flaticon2-fast-back',
		last: 'flaticon2-fast-next',
		more: 'flaticon-more-1',
	},
	rowDetail: {
	    expand: 'fa fa-caret-down', 
	    collapse: 'fa fa-caret-right'
	},
}
sortable 是否支持按列排序 默认 true
resizable
是否支持鼠标拖动改变列宽 默认 false
filterable 在列中过滤 默认 false
pagination
显示分页信息 默认 true
editable
行内编辑 默认 false
columns
见本文下方
search
搜索
{
	// 按回车键确认
	onEnter: false,
	// 文本内容
	input: null,
	// 搜索延时
	delay: 400,
	// 键名
	key: null
}

layout.columns:

属性 功能 解释
field 字段名 对应 JSON 的属性名,点击表头时作为排序字段名
title 表头名 显示在表格头部
sortable 默认排序方式 可选:'asc' / 'desc'
width 单元格最小宽度 值与 CSS 值一致,填数字时默认单位 px
type 数据类型 'number' / 'date' 等,与本地排序有关
format 数据格式化 例格式化日期:'YYYY-MM-DD'
selector 是否显示选择框 布尔值或对象,如:{ class: '' }
textAlign 文字对齐方式 'center'
overflow 内容超过单元格宽度时是否显示 'visible':永远显示
autoHide 自适应显示/隐藏 布尔值
template 用于显示内容的 HTML 模板 function(row) { return row.Id; }
sortCallback 排序回调 自定义排序方式,参 local-sort.js

其它:

属性 功能 解释
translate 翻译

参 core.datatable.js 3512 行,简体中文示例:

translate: {
    records: {
        processing: '加载中...',
        noRecords: '没有找到相关内容',
    },
    toolbar: {
        pagination: {
            items: {
                default: {
                    first: '首页',
                    prev: '前一页',
                    next: '后一页',
                    last: '末页',
                    more: '更多',
                    input: '请输入跳转页码',
                    select: '设置每页显示项数',
                },
                info: '当前第 {{start}} - {{end}} 项 共 {{total}} 项',
            },
        },
    },
},


extensions

暂时没有找到对字符串内容进行自动 HTML 编码的属性,这可能带来 XSS 攻击风险,在 remote 方式中必须在服务端预先 HtmlEncode。即使在 layout.columns.template 中进行处理也是无济于事,恶意代码会在 ajax 加载完成后立即执行。


方法和事件:待完善。


更多信息请查询官方文档:https://keenthemes.com/keen/?page=docs&section=html-components-datatable

xoyozo 5 年前
5,411

ASP.NET MVC 使用 Authorize 过滤器验证用户登录。Authorize 过滤器首先运行在任何其它过滤器或动作方法之前,主要用来做登录验证或者权限验证。

示例:使用 Authorize 过滤器实现简单的用户登录验证。

1、创建登录控制器 LoginController

/// <summary>
/// 登录控制器
/// </summary>
[AllowAnonymous]
public class LoginController : Controller
{
    /// <summary>
    /// 登录页面
    /// </summary>
    public ActionResult Index()
    {
        return View();
    }
 
    /// <summary>
    /// 登录
    /// </summary>
    [HttpPost]
    public ActionResult Login(string loginName, string loginPwd)
    {
        if (loginName == "admin" && loginPwd == "123456")
        {
            // 登录成功
            Session["LoginName"] = loginName;
            return RedirectToAction("Index", "Home");
        }
        else
        {
            // 登录失败
            return RedirectToAction("Index", "Login");
        }
    }
 
    /// <summary>
    /// 注销
    /// </summary>
    public ActionResult Logout()
    {
        Session.Abandon();
        return RedirectToAction("Index", "Login");
    }
}

注意:在登录控制器 LoginController 上添加 AllowAnonymous 特性,该特性用于标记在授权期间要跳过 AuthorizeAttribute 的控制器和操作。

2、创建登录页面

@{
    ViewBag.Title = "登录页面";
    Layout = null;
}
 
<h2>登录页面</h2>
 
<form action='@Url.Action("Login","Login")' id="form1" method="post">
    用户:<input type="text" name="loginName" /><br />
    密码:<input type="password" name="loginPwd" /><br />
    <input type="submit" value="登录">
</form>

效果图:

3、创建主页控制器 LoginController

public class HomeController : Controller
{
    public ActionResult Index()
    {
        // 获取当前登录用户
        string loginName = Session["LoginName"].ToString();
        ViewBag.Message = "当前登录用户:" + loginName;
        return View();
    }
}

4、创建主页页面

@{
    ViewBag.Title = "Index";
    Layout = null;
}
 
<h2>Index</h2>
<h3>@ViewBag.Message</h3>
<a href="@Url.Action("Logout","Login")">注销</a>

效果图:

5、创建授权过滤器 LoginAuthorizeAttribute 类

创建 Filter 目录,在该目录下创建授权过滤器 LoginAuthorizeAttribute 类,继承 AuthorizeAttribute。

using System.Web.Mvc;
 
namespace MvcApp.Filter
{
    /// <summary>
    /// 授权过滤器
    /// </summary>
    public class LoginAuthorizeAttribute : AuthorizeAttribute
    {
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            // 判断是否跳过授权过滤器
            if (filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true)
               || filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true))
            {
                return;
            }
 
            // 判断登录情况
            if (filterContext.HttpContext.Session["LoginName"] == null || filterContext.HttpContext.Session["LoginName"].ToString()=="")
            {
                //HttpContext.Current.Response.Write("认证不通过");
                //HttpContext.Current.Response.End();
 
                filterContext.Result = new RedirectResult("/Login/Index");
            }
        }
    }
}

通常 Authorize 过滤器也是在全局过滤器上面的,在 App_Start 目录下的 FilterConfig 类的 RegisterGlobalFilters 方法中添加:

using System.Web;
using System.Web.Mvc;
using MvcApp.Filter;
 
namespace MvcApp
{
    public class FilterConfig
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new HandleErrorAttribute());
 
            // 添加全局授权过滤器
            filters.Add(new LoginAuthorizeAttribute());
        }
    }
}

Global.asax 下的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
 
namespace MvcApp
{
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
        }
    }
}


xoyozo:暂时没有按区域(Area)来统一配置的方法,建议加在 Controller 上。https://stackoverflow.com/questions/2319157/how-can-we-set-authorization-for-a-whole-area-in-asp-net-mvc


p
转自 pan_junbiao 5 年前
4,489

image.png

Unify Template 提供三种文件上传样式(如上图),“Plain File Input”和“File input”使用传统 <form /> 提交,“Advanced File input”调用组件实现。


ASP.NET Core 实现传统 <form /> 提交(以“Plain File Input”为例):

<form class="g-brd-around g-brd-gray-light-v4 g-pa-30 g-mb-30" id="form_avatar_upload" asp-action="AvatarUpload" enctype="multipart/form-data" method="post">
    <!-- Plain File Input -->
    <div class="form-group mb-0">
        <p>Plain File Input</p>
        <label class="u-file-attach-v2 g-color-gray-dark-v5 mb-0">
            <input id="fileAttachment" name="Avatar" type="file" onchange="form_avatar_upload.submit()">
            <i class="icon-cloud-upload g-font-size-16 g-pos-rel g-top-2 g-mr-5"></i>
            <span class="js-value">Attach file</span>
        </label>
    </div>
    <!-- End Plain File Input -->
</form>
public class AvatarUploadModel
{
    public IFormFile Avatar { get; set; }
}

[HttpPost]
public IActionResult AvatarUpload(AvatarUploadModel Item)
{
    return RedirectToAction("");
}


ASP.NET Core 调用 HSFileAttachment 组件实现:

<div class="form-group mb-20">
    <label class="g-mb-10">上传作品文件(包)</label>
    <input class="js-file-attachment_project" type="file" name="" id="file_project">
    <div style="display: none;" id="file_project_tip">
        <div class="u-file-attach-v3 g-mb-15" id="file_project_tip_content">
            <h3 class="g-font-size-16 g-color-gray-dark-v2 mb-0">拖曳文件到此处或者 <span class="g-color-primary">浏览你的电脑</span></h3>
            <p class="g-font-size-14 g-color-gray-light-v2 mb-0">
                如果您的作品包含多个文件,请添加到压缩包再上传
            </p>
            <p class="g-font-size-14 g-color-gray-light-v2 mb-0">
                支持的文件格式:
                <code>zip</code>
                <code>rar</code>
                <code>7z</code>
                <code>psd</code>
                <code>ai</code>
                <code>cdr</code>
                <code>jpg</code>
                <code>png</code>
                <code>gif</code>
                <code>tif</code>
                <code>webp</code>
            </p>
            <p class="g-font-size-14 g-color-gray-light-v2 mb-0">文件(包)的大小不能超过 <span>@options.Value.MaxSizeOfSigleFile_MB</span> MB</p>
        </div>
    </div>
    <small class="form-control-feedback">请上传作品文件</small>
</div>

<!-- JS Implementing Plugins -->
<script src="//cdn.***.com/unify/unify-v2.6.2/html/assets/vendor/jquery.filer/js/jquery.filer.min.js"></script>
<!-- JS Unify -->
<script src="//cdn.***.com/unify/unify-v2.6.2/html/assets/js/helpers/hs.focus-state.js"></script>
<script src="//cdn.***.com/unify/unify-v2.6.2/html/assets/js/components/hs.file-attachement.js"></script>
<!-- JS Plugins Init. -->
<script>
    $(document).on('ready', function () {
        // initialization of forms
        $.HSCore.components.HSFileAttachment.init('.js-file-attachment_project', {
            changeInput: $('#file_project_tip').html(),
            uploadFile: {
                url: url_UploadFile,
                data: { type: 'project' },
                success: function (data, element) {
                    console.log(data);

                    if (!data.success) {
                        var parent = element.find(".u-progress-bar-v1").parent();
                        element.find(".u-progress-bar-v1").fadeOut("slow", function () {
                            $("<div class=\"text-error g-px-10\"><i class=\"fa fa-minus-circle\"></i> Error</div>").hide().appendTo(parent).fadeIn("slow");
                        });

                        alert(data.err_msg);
                    } else {
                        var parent = element.find(".u-progress-bar-v1").parent();
                        element.find(".u-progress-bar-v1").fadeOut("slow", function () {
                            $("<div class=\"text-success g-px-10\"><i class=\"fa fa-check-circle\"></i> Success</div>").hide().appendTo(parent).fadeIn("slow");
                        });

                        $('#file_project_tip_content').slideUp();

                        upload_project_id = data.id;
                    }
                }
            }
        });
        $.HSCore.helpers.HSFocusState.init();
    });
</script>
public IActionResult UploadFile([FromForm(Name = "[]")]IFormFile file, string type)
{
    return Ok(new { id = 1 });
}


xoyozo 5 年前
3,647

使用 JS 根据屏幕宽度来计算页面尺寸和字体大小并不是一个好办法。

rem 单位是跟随网页根元素的 font-size 改变而自动改变的,是一个相对的长度单位,非常适合在不同手机界面上自适应屏幕大小。 


一般的手机浏览器的默认字体大小为 16px。即:

:root { font-size: 16px; } /* 在 HTML 网页中,:root 选择器相当于 html */

也就是说,如果我定义一个宽度为 1rem,那么它的实际宽度为 16px。反过来说,如果设计稿宽度为 1000px,那么相当于 1000÷16=62.5‬rem。因此,我们设置一个 62.5rem 的宽度,即可正常显示 1000px 的设计效果。


为了适应不同屏幕尺寸的手机,只需按比例换算更改 :root 的 font-size 即可。这个比例是由“窗口宽度 ÷ 设计稿宽度”得到的,姑且称它为“窗设比”。

以 iPhone X 逻辑宽度 375px 为例,设计稿宽度 1000px 时,窗设比为:0.375。:root 的 font-size 将会被重置为 16 * 0.375 = 6px。上面的 62.5rem 宽度将对应 iPhone X 宽度为 62.5 * 6 = 375px,正好是屏幕逻辑宽度。


根据这个思路,理论上,我们只需要使用 CSS 将 :root 的 font-size 设置为 1px,然后使用 JS 重置为与“窗设比”相乘的积(本例中将被重置为 0.375px)。这样,我们可以不通过换算,只需要更改单位,将设计稿中的 500px 直接写入到 CSS 为 500rem,即可实现在 iPhone X 上显示逻辑宽度为(500 * 0.375 =)187.5px,即设计稿中的 1/2 宽度对应到屏幕上 1/2 的宽度。


实际上,部分安卓手机(如华为某旗舰机)不支持 :root 的 font-size 小于 12px,其它一些浏览器也会出现小于 12px 的文字以 12px 显示。


为了避免在任何环节出现小于 12px 的 font-size 值,且不增加设计稿尺寸到 CSS 数值的换算难度,我们设计了以下思路:

  1. 约定“设样比(设计稿的 px 值与 CSS 的 rem 值的比)”为 100;

  2. 使用 JS 将 :root 的 font-size 重置为“设样比”与“窗设比(窗口宽度 ÷ 设计稿宽度)”的乘积;

  3. 计算 CSS 时的 rem 值只需从设计稿中获取 px 值再除以“设样比”100 即可。 


这样做的另一个好处是,不用要求设计师必须按多少尺寸来设计,当然按主流尺寸来建议是可行的。


完整代码(jQuery 版):

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0" />
    <title>Index</title>
    <style>
        body { margin: 0; text-align: center; }
        #app { margin: 0 auto; background-color: #EEE; display: none; }
        /* 长度计算方法:从设计稿获得长度(例如 160px),除以 px2rem 值(本例为 100),得到 1.6rem */
        .whole { background-color: #D84C40; width: 10rem; }
        .half { background-color: #3296FA; width: 5rem; }
        .half2 { background-color: #3296FA; width: 5rem; margin-left: 5rem; }
    </style>
</head>
<body>
    <div id="app">
        <div class="whole">100%</div>
        <div class="half">50%</div>
        <div class="half2">50%</div>
    </div>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script>
        // [函数] 重置尺寸
        function fn_resize() {
            // 设计稿宽度(px)
            var designsWidth = 1000;
            // 约定的“设计稿 px 值与 CSS rem 值的比”,为方便计算,一般无需改动
            var px2rem = 100;
            // 限制 #app 的最大宽度为设计稿宽度
            $('#app').css('max-width', designsWidth).css('min-height', $(window).height());
            // 重置 :root 的 font-size
            var appWidth = Math.min($(window).width(), designsWidth);
            $(':root').css('font-size', px2rem * appWidth / designsWidth);
            $('#app').show();
        }
        // 页面加载完成后重置尺寸
        $(fn_resize);
        // 改变窗口大小时重置尺寸
        $(window).resize(fn_resize);
    </script>
</body>
</html>

完整代码(Vue2 版):

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0" />
    <title>Index</title>
    <style>
        body { margin: 0; text-align: center; }
        #app { margin: 0 auto; background-color: #EEE; }
        /* 长度计算方法:从设计稿获得长度(例如 160px),除以 px2rem 值(本例为 100),得到 1.6rem */
        .whole { background-color: #D84C40; width: 10rem; }
        .half { background-color: #3296FA; width: 5rem; }
        .half2 { background-color: #3296FA; width: 5rem; margin-left: 5rem; }
    </style>
</head>
<body>
    <div id="app" v-bind:style="{ maxWidth: designsWidth + 'px', minHeight: appMinHeight + 'px' }" style="display: none;" v-show="appShow">
        <div class="whole">100%</div>
        <div class="half">50%</div>
        <div class="half2">50%</div>
        <div>appWidth: {{ appWidth }}</div>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
    <script>
        var app = new Vue({
            el: '#app',
            data: {
                // 设计稿宽度(px)
                designsWidth: 1000,
                // 约定的“设计稿 px 值与 CSS rem 值的比”,为方便计算,一般无需改动
                px2rem: 100,
                // #app 的 width
                appWidth: 0, // 勿改
                appMinHeight: 0, // 勿改
                appShow: false, // 勿改
            },
            mounted: function () {
                // 页面加载完成后重置尺寸
                this.fn_resize();
                const that = this;
                // 改变窗口大小时重置尺寸
                window.onresize = () => {
                    return (() => {
                        console.log('RUN window.onresize()');
                        that.fn_resize();
                    })();
                };
            },
            watch: {
                // 侦听 appWidth 更改 root 的 font-size
                appWidth: function () {
                    console.log('RUN watch: appWidth');
                    var root = document.getElementsByTagName("html")[0];
                    root.style.fontSize = (this.px2rem * this.appWidth / this.designsWidth) + 'px';
                    this.appShow = true;
                }
            },
            methods: {
                fn_resize: function () {
                    console.log('RUN methods: fn_resize()');
                    this.appWidth = Math.min(document.body.clientWidth, this.designsWidth);
                    this.appMinHeight = document.documentElement.clientHeight;
                }
            }
        });
    </script>
</body>
</html>

完整代码(Vue3 版):

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0" />
    <title>Index</title>
    <style>
        body { margin: 0; text-align: center; }
        #app > div { display: none; margin: 0 auto; background-color: #EEE; }
        /* 长度计算方法:从设计稿获得长度(例如 160px),除以 px2rem 值(本例为 100),得到 1.6rem */
        .whole { background-color: #D84C40; width: 10rem; }
        .half { background-color: #3296FA; width: 5rem; }
        .half2 { background-color: #3296FA; width: 5rem; margin-left: 5rem; }
    </style>
</head>
<body>
    <div id="app">
        <div style="display: none;" v-bind:style="{ maxWidth: designsWidth + 'px', minHeight: appMinHeight + 'px', display: appShow ? 'block' : 'none' }">
            <div class="whole">100%</div>
            <div class="half">50%</div>
            <div class="half2">50%</div>
            <div>appWidth: {{ appWidth }}</div>
        </div>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@3"></script>
    <script>
        const app = Vue.createApp({
            data: function () {
                return {
                    // 设计稿宽度(px)
                    designsWidth: 1000,
                    // 约定的“设计稿 px 值与 CSS rem 值的比”,为方便计算,一般无需改动
                    px2rem: 100,
                    // #app 的 width
                    appWidth: 0, // 勿改
                    appMinHeight: 0, // 勿改
                    appShow: false, // 勿改
                }
            },
            mounted: function () {
                // 页面加载完成后重置尺寸
                this.fn_resize();
                const that = this;
                // 改变窗口大小时重置尺寸
                window.onresize = () => {
                    return (() => {
                        console.log('RUN window.onresize()');
                        that.fn_resize();
                    })();
                };
            },
            watch: {
                // 侦听 appWidth 更改 root 的 font-size
                appWidth: function () {
                    console.log('RUN watch: appWidth');
                    var root = document.getElementsByTagName("html")[0];
                    root.style.fontSize = (this.px2rem * this.appWidth / this.designsWidth) + 'px';
                    this.appShow = true;
                }
            },
            methods: {
                fn_resize: function () {
                    console.log('RUN methods: fn_resize()');
                    this.appWidth = Math.min(document.body.clientWidth, this.designsWidth);
                    this.appMinHeight = document.documentElement.clientHeight;
                }
            }
        });
        const vm = app.mount('#app');
    </script>
</body>
</html>


示例中 CSS 初始 :root 的 font-size 为 16px(一个较小值,防止页面加载时瞬间出现大号文字影响用户体验),经过 fn_resize 后,:root 的 font-size 被设置为(100 * 375 / 1000 =)37.5px(iPhone X 中),那么宽度为 1000px 的设计稿中的 500px 换算到 CSS 中为 5rem,也即 37.5 * 5 = 187.5px,就是 iPhone X 的屏幕宽度的一半。

示例中 id 为 app 的 div 是用来在 PC 浏览器中限制页面内容最大宽度的(类似微信公众号发布的文章),如果网页不需要在 PC 端显示,jQuery 版代码中的 $('#app').width() 可以用 $(window).width() 来代替。

这个 div#app 一般设计有背景色,用于在宽度超过设计稿的设备上显示时区别于 body 的背景。但是当网页内容不超过一屏时,div#app 高度小于窗口,示例中与 appMinHeight 相关的代码就是为了解决这个问题。

示例中 div#app 隐藏/显示相关代码用于解决在页面加载初期由于 font-size 值变化引起的一闪而过的排版错乱。

image.png


最后补充一点,如果改变窗口大小时涉及执行耗时的操作,为避免页面卡顿,可以参考这篇文章添加函数防抖:https://xoyozo.net/Blog/Details/js-function-anti-shake

xoyozo 5 年前
7,452


引用

jQuery、moment.js、daterangepicker


例子

$('.x_dates').daterangepicker({
    "timePicker": false, // 是否显示时间
    //"dateLimit": {
    //    "days": 7 // 可选中的最大区间(天)
    //},
    "ranges": { // 快捷栏
        "今天": [moment(), moment()],
        "昨天": [moment().subtract(1, 'days'), moment().subtract(1, 'days')],
        "最近 7 天": [moment().subtract(6, 'days'), moment()],
        "最近 30 天": [moment().subtract(29, 'days'), moment()],
        "本月": [moment().startOf('month'), moment().endOf('month')],
        "上个月": [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')]
    },
    startDate: daterangepicker_startDate, // moment(),
    endDate: daterangepicker_endDate, // moment(),
    autoUpdateInput: true,
    "locale": {
        "direction": "ltr",
        "format": "YYYY-MM-DD", // YYYY-MM-DD HH:mm
        "separator": " 至 ",
        "applyLabel": "确定",
        "cancelLabel": "取消",
        "fromLabel": "From",
        "toLabel": "To",
        "customRangeLabel": "自定义",
        "daysOfWeek": ["日", "一", "二", "三", "四", "五", "六"],
        "monthNames": ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
        "firstDay": 1
    }
}, function (start, end, label) {
    //console.log('New date range selected: ' + start.format('YYYY-MM-DD') + ' to ' + end.format('YYYY-MM-DD') + ' (predefined range: ' + label + ')');
    fn_daterangepicker_changed(start.format('YYYY-MM-DD'), end.format('YYYY-MM-DD'))
});


官网(含配置工具)

http://www.daterangepicker.com/


GitHub

https://github.com/dangrossman/daterangepicker


配置工具

下载的包中的 demo.html


Demo

https://awio.iljmp.com/5/drpdemo


xoyozo 5 年前
5,001