博客 (115)

制作类似下图中的拖拽排序功能:

image.png

1. 首先数据库该表中添加字段 sort,类型为 double(MySQL 中为 double(0, 0))。

2. 页面输出绑定数据(以 ASP.NET MVC 控制器为例):

public ActionResult EditSort()
{
    if (!zConsole.Logined) { return RedirectToAction("", "SignIn", new { redirect = Request.Url.OriginalString }); }

    db_auto2018Entities db = new db_auto2018Entities();

    return View(db.dt_dealer.Where(c => c.enabled).OrderBy(c => c.sort));
}

这里可以加条件列出,即示例中 enabled == true 的数据。

3. 前台页面引用 jQuery 和 jQuery UI。

4. 使用 <ul /> 列出数据:

<ul id="sortable" class="list-group gutter list-group-lg list-group-sp">
    @foreach (var d in Model)
    {
        <li class="list-group-item" draggable="true" data-id="@d.id">
            <span class="pull-left"><i class="fa fa-sort text-muted fa m-r-sm"></i> </span>
            <div class="clear">
                【id=@d.id】@d.name_full
            </div>
        </li>
    }
</ul>

5. 初始化 sortable,当拖拽结束时保存次序:

<script>
    var url_SaveSort = '@Url.Action("SaveSort")';
</script>
<script>
    $("#sortable").sortable({
        stop: function (event, ui) {
            // console.log('index: ' + $(ui.item).index())
            // console.log('id: ' + $(ui.item).data('id'))
            // console.log('prev_id: ' + $(ui.item).prev().data('id'))
            $.post(url_SaveSort, {
                id: $(ui.item).data('id'),
                prev_id: $(ui.item).prev().data('id')
            }, function (json) {
                if (json.result.success) {
                    // window.location.reload();
                } else {
                    toastr["error"](json.result.info);
                }
            }, 'json');
        }
    });
    $("#sortable").disableSelection();
</script>

这里回传到服务端的参数为:当前项的 id 值、拖拽后其前面一项的 prev_id 值(若移至首项则 prev_id 为 undefined)。

不使用 $(ui.item).index() 是因为,在有筛选条件的结果集中排序时,使用该索引值配合 LINQ 的 .Skip 会引起取值错误。

6. 控制器接收并保存至数据库:

[HttpPost]
public ActionResult SaveSort(int id, int? prev_id)
{
    if (!zConsole.Logined)
    {
        return Json(new { result = new { success = false, msg = "请登录后重试!" } }, JsonRequestBehavior.AllowGet);
    }

    db_auto2018Entities db = new db_auto2018Entities();

    dt_dealer d = db.dt_dealer.Find(id);

    // 拖拽后其前项 sort 值(若无则 null)(此处不需要加 enabled 等筛选条件)
    double? prev_sort = prev_id.HasValue
        ? db.dt_dealer.Where(c => c.id == prev_id).Select(c => c.sort).Single()
        : null as double?;

    // 拖拽后其后项 sort 值(若无前项则取首项作为后项)(必须强制转化为 double?,否则无后项时会返回 0,导致逻辑错误)
    double? next_sort = prev_id.HasValue
        ? db.dt_dealer.Where(c => c.sort > prev_sort && c.id != id).OrderBy(c => c.sort).Select(c => (double?)c.sort).FirstOrDefault()
        : db.dt_dealer.Where(c => c.id != id).OrderBy(c => c.sort).Select(c => (double?)c.sort).FirstOrDefault();

    if (prev_sort.HasValue && next_sort.HasValue)
    {
        d.sort = (prev_sort.Value + next_sort.Value) / 2;
    }
    if (prev_sort == null && next_sort.HasValue)
    {
        d.sort = next_sort.Value - 1;
    }
    if (prev_sort.HasValue && next_sort == null)
    {
        d.sort = prev_sort.Value + 1;
    }

    db.SaveChanges();

    return Json(new { item = new { id = d.id }, result = new { success = true } });
}

需要注意的是,当往数据库添加新项时,必须将 sort 值设置为已存在的最大 sort 值 +1 或最小 sort 值 -1。

var d = new dt_dealer
{
    name_full = "新建项",
    sort = (db.dt_dealer.Max(c => (double?)c.sort) ?? 0) + 1,
};


xoyozo 6 年前
6,487

ASP.NET MVC 如果从控制器输出的是匿名类的集合,那么绑定到视图时会报错:

“object”未包含“xxx”的定义

简单的解决方法是 JSON 序列化再反序列化,例如:

ViewBag.list = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(q.ToList()));

上面是 ViewBag 的情形,如果是直接 return View(匿名类集合);

那么在视图页面:

@model IEnumerable<dynamic>


xoyozo 7 年前
3,735

Font Awesome 从 5.0 版本开始,在保持经典的 Web Fonts with CSS 外,还提供更现代化,功能更强大的 SVG with JS 部署方式。


升级为 SVG with JS 后,如果部分图标不显示,可能的原因是:

· 部分在老版本中的图标不再 5.0 版本上提供,解决的方法是引用 fa-v4-shims.js;

· 此方式把 <i /> 标签通过 JS 替换为 <svg /> 标签,须要改动 CSS 样式。


如果仍然使用 Web Fonts with CSS,部分图标不显示,可能的原因是:

· 新版本不再推荐以 fa 的 class 引用,对 Solid(实心)、Regular(标准)、Light(简洁)、Brands(品牌)四大分类分别使用新的命名方式:fas、far、fal、fab。具体可以在 Font Awesome 官网上找到相应图标后,点击打开图标详情,复制已经生成的 HTML 代码。fa 将被视为 fas。

xoyozo 7 年前
7,124

1.1 四大特性

MVC

<!doctype html>
<html ng-app>
<body>
    <div ng-controller="HelloAngular">
        <p>{{greeting.text}}, Angular</p>
    </div>
    <script src="js/angular-1.3.0.js"></script>
    <script>
        function HelloAngular($scope) {
            $scope.greeting = {
                text: 'Hello'
            };
        }
    </script>
</body>
</html>

模块化与依赖注入

<!doctype html>
<html ng-app="app">
<body>
    <div ng-controller="HelloAngular">
        <p>{{greeting.text}}, Angular</p>
    </div>
    <script src="js/angular-1.3.0.js"></script>
    <script>
        var myModule = angular.module("app", []);

        myModule.controller("HelloAngular", ['$scope',
            function($scope) {
                $scope.greeting = {
                    text: 'Hello'
                };
            }
        ]);
    </script>
</body>
</html>

双向数据绑定

<!doctype html>
<html ng-app>
<body>
    <input ng-model="greeting.text" />
    <p>{{greeting.text}},AngularJS</p>
    <script src="js/angular-1.3.0.js"></script>
</body>
</html>

指令

<!doctype html>
<html ng-app="app">
<body>
    <hello></hello>
    <script src="js/angular-1.3.0.js"></script>
    <script>
        var myModule = angular.module("app", []);
        myModule.directive("hello", function () {
            return {
                restrict: 'E',
                template: '<div>Hi everyone!</div>',
                replace: true
            }
        });
    </script>
</body>
</html>


1.2 开发环境

node.js
JS 运行环境
Bower插件管理(相当于 NuGet)
Grunt代码混淆、合并工具
Sublime代码编辑器(小巧),Zen Coding 快速编写 HTML/CSS 代码
WebStorm代码编辑器(强大)
git 小乌龟版本管理


2.1 MVC

Controller 使用要点:

  • 不要复用 Controller

  • 不要操作 DOM

  • 不要做数据格式化,ng 有表单控件

  • 不要做数据过滤,ng 有 $filter


$scope 作用域

<!doctype html>
<html ng-app>
<head>
    <style>
        .show-scope-demo .ng-scope, .show-scope-demo .ng-scope { border: 1px solid red; margin: 3px; }
    </style>
</head>
<body>
    <div class="show-scope-demo">
        <div ng-controller="GreetCtrl">
            Hello {{name}}!
        </div>
        <div ng-controller="ListCtrl">
            <ol>
                <li ng-repeat="name in names">
                    {{name}} from {{department}}
                </li>
            </ol>
        </div>
    </div>
    <script src="js/angular-1.3.0.js"></script>
    <script>
        function GreetCtrl($scope, $rootScope) {
            $scope.name = 'World';
            // 在根作用域定义了 department,在 ListCtrl 控制器中也能用
            $rootScope.department = 'Angular';
        }

        function ListCtrl($scope) {
            $scope.names = ['Igor', 'Misko', 'Vojta'];
        }
    </script>
</body>
</html>
<!doctype html>
<html ng-app>
<head>
    <style>
        .show-scope-demo .ng-scope, .show-scope-demo .ng-scope { border: 1px solid red; margin: 3px; }
    </style>
</head>
<body>
    <div ng-controller="EventController">
        Root scope
        <tt>MyEvent</tt> count: {{count}}
        <ul>
            <li ng-repeat="i in [1]" ng-controller="EventController">
                <button ng-click="$emit('MyEvent')">
                    $emit('MyEvent')
                </button>
                <button ng-click="$broadcast('MyEvent')">
                    $broadcast('MyEvent')
                </button>
                <br>
                Middle scope
                <tt>MyEvent</tt> count: {{count}}
                <ul>
                    <li ng-repeat="item in [1, 2]" ng-controller="EventController">
                        Leaf scope
                        <tt>MyEvent</tt> count: {{count}}
                    </li>
                </ul>
            </li>
        </ul>
    </div>
    <script src="js/angular-1.3.0.js"></script>
    <script>
        function EventController($scope) {
            $scope.count = 0;
            $scope.$on('MyEvent', function () {
                $scope.count++;
            });
        }

    </script>
</body>
</html>

$emit:向上传播

$broadcast:向下传播

ng-repeat="i in [1, 2]":循环固定项

$scope 是一个树形结构,与 DOM 平行

子 $scope 对象会继承父 $scope 上的属性和方法

每个 Angular 应用只有一个根 $scope,一般位于 ng-app 上

$scope 可以传播事件,类似 DOM 事件,可向上可向下

 

2.2 路由、模块、依赖注入

var bookStoreApp = angular.module('bookStoreApp', [
    'ngRoute', 'ngAnimate', 'bookStoreCtrls', 'bookStoreFilters',
    'bookStoreServices', 'bookStoreDirectives'
]);

bookStoreApp.config(function($routeProvider) {
    $routeProvider.when('/hello', {
        templateUrl: 'tpls/hello.html',
        controller: 'HelloCtrl'
    }).when('/list',{
    	templateUrl:'tpls/bookList.html',
    	controller:'BookListCtrl'
    }).otherwise({
        redirectTo: '/hello'
    })
});


2.3 双向数据绑定

绑定方式1:<p>{{greeting.text}}, Angular</p>

绑定方式2:<p><span ng-bind="greeting.text"></span>, Angular</p>

方式1 在加载 angular.js 之前会将 {{***}} 显示于界面,因此在 index.html 页面建议用方式2,通过模板加载的页面上的绑定可以使用方式1。

<!doctype html>
<html ng-app="UserInfoModule">

<head>
    <meta charset="utf-8">
    <link rel="stylesheet" href="css/bootstrap-3.0.0/css/bootstrap.css">
</head>

<body>
    <div class="panel panel-primary">
        <div class="panel-heading">
            <div class="panel-title">双向数据绑定</div>
        </div>
        <div class="panel-body">
            <div class="row">
                <div class="col-md-12">
                    <form class="form-horizontal" role="form" ng-controller="UserInfoCtrl">
                        <div class="form-group">
                            <label class="col-md-2 control-label">
                                邮箱:
                            </label>
                            <div class="col-md-10">
                                <input type="email" class="form-control" placeholder="推荐使用126邮箱" ng-model="userInfo.email">
                            </div>
                        </div>
                        <div class="form-group">
                            <label class="col-md-2 control-label">
                                密码:
                            </label>
                            <div class="col-md-10">
                                <input type="password" class="form-control" placeholder="只能是数字、字母、下划线" ng-model="userInfo.password">
                            </div>
                        </div>
                        <div class="form-group">
                            <div class="col-md-offset-2 col-md-10">
                                <div class="checkbox">
                                    <label>
                                        <input type="checkbox" ng-model="userInfo.autoLogin">自动登录
                                    </label>
                                </div>
                            </div>
                        </div>
                        <div class="form-group">
                            <div class="col-md-offset-2 col-md-10">
                                <button class="btn btn-default" ng-click="getFormData()">获取Form表单的值</button>
                                <button class="btn btn-default" ng-click="setFormData()">设置Form表单的值</button>
                                <button class="btn btn-default" ng-click="resetForm()">重置表单</button>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
    <script src="js/angular-1.3.0.js"></script>
    <script>
        var userInfoModule = angular.module('UserInfoModule', []);
        userInfoModule.controller('UserInfoCtrl', ['$scope',
            function ($scope) {
                $scope.userInfo = {
                    email: "253445528@qq.com",
                    password: "253445528",
                    autoLogin: true
                };
                $scope.getFormData = function () {
                    console.log($scope.userInfo);
                };
                $scope.setFormData = function () {
                    $scope.userInfo = {
                        email: 'damoqiongqiu@126.com',
                        password: 'damoqiongqiu',
                        autoLogin: false
                    }
                };
                $scope.resetForm = function () {
                    $scope.userInfo = {
                        email: "253445528@qq.com",
                        password: "253445528",
                        autoLogin: true
                    };
                }
            }
        ]);
    </script>
</body>
</html>

注意 ng-click 的用法。

ng-class 用法:

ng-class='{bg-danger: isError, bg-success: isSuccess}'

表示当 isError 为 true 时应用 bg-danger 样式,当 isSuccess 为 true 时应用 bg-success 样式。

ng-show / ng-hide 用法:

ng-show='isShow'

表示当 isShow 为 true 时显示,否则隐藏。


2.4 指令(Directive)重要!

路由

Angular 自带的 angular.route.js 不能实现嵌套路由,推荐使用 UI-Router,方便将页面分割成多个 ui-view 实现局部更新。

angular.module("app", []).directive("directiveName", function () {
    return {
        //通过设置项来定义 
    };
});

设置项:

restrict: 'AEMC', // A 属性(默认),E 元素,M 注释,C 样式

template: '<div>xxx</div>', // 替代的 HTML

replace: true, // 替代

transclude: true, // 嵌套,需要 template 配合,例如:

templace: '<div>xxx <div ng-transclude></div></div>', // 原来指令标签中的内容会被放置于 ng-transclude 所在 div 中。

templaceUrl: 'xxx.html', // 使用独立 html 的模板内容

compile: function() { }, // 对模板自身进行转换

link: function() {}, // 操作 DOM、绑定事件监听器


可以在页面中使用 ui-sref 或控制器中 $state.go 来实现跳转(页面更新)

如何传递参数:

一个简单的示例:在一个文章列表页面和一个文章详情页面中实现跳转,需要传递参数文章 id

  1. 在路由配置 state 中加入:params: { "id": null }

  2. 在列表行中绑定 id,格式如:<a ui-sref="app.blog.item({id:row.id})"><span>Edit</span></a>

  3. 在详情页控制器中接收参数:$stateParams,即可用 $stateParams.id 来获取文章 id 值

  4. 如果需要同时更改地址栏 url,并且能够使用该 url 直接打开该文章,那么修改 state 中的 url 为 /item?id,体现为 #/app/blog/item?id=9,也可以用冒号 /item:id,体现为 #/app/blog/item9

参:URL Parameters


scope 的绑定策略:

@:把当前属性作为字符串传递。你还可以绑定来自外层 scope 的值,在属性值中插入 {{ }} 即可

=:与父 scope 中的属性进行双向绑定

&:传递一个来自父 scope 的函数,稍后调用

ERP 类型的系统必备 UI 组件:MiniUIExt JS

互联网/电商型系统必备 UI 组件:KISSY

其它常用组件:AngularUI


2.5 Service 与 Provider

使用 $http.get 获取 JSON 数据

<!doctype html>
<html ng-app="app">
<head>
    <meta charset="UTF-8">
    <script src="framework/1.3.0.14/angular.js"></script>
</head>
<body>
    <div ng-controller="LoadDataCtrl">
        <ul>
            <li ng-repeat="user in users">
                {{user.name}}
            </li>
        </ul>
    </div>
    <script>
        var app = angular.module("app", []);
        app.controller('LoadDataCtrl', ['$scope', '$http', function ($scope, $http) {
            $http({
                method: 'GET',
                url: 'data.json'
            }).success(function (data, status, headers, config) {
                console.log("success...");
                console.log(data);
                $scope.users = data;
            }).error(function (data, status, headers, config) {
                console.log("error...");
            })
        }]);
    </script>
</body>
</html>

data.json:

[
	{ "name": "a" },
	{ "name": "b" },
	{ "name": "c" }
]

自定义 Service

<!doctype html>
<html ng-app="app">
<head>
    <meta charset="UTF-8">
    <script src="framework/1.3.0.14/angular.js"></script>
</head>
<body>
    <div ng-controller="ServiceController">
        <label>用户名:</label>
        <input type="text" ng-model="username" placeholder="请输入用户名" />
        <ul ng-show="username">
            <li ng-repeat="user in users">
                {{user.name}}
            </li>
        </ul>
    </div>
    <script>
        var app = angular.module("app", []);

        app.factory('userListService', ['$http', function ($http) {
            var doRequest = function (username, path) {
                return $http({ method: 'GET', url: 'data.json' });
            };
            return {
                userList: function (username) {
                    return doRequest(username, 'userList');
                }
            }
        }]);

        app.controller('ServiceController', ['$scope', '$timeout', 'userListService', function ($scope, $timeout, userListService) {
            var timeout;
            $scope.$watch('username', function (newUserName) {
                if (newUserName) {
                    if (timeout) {
                        $timeout.cancel(timeout);
                    }
                    timeout = $timeout(function () {
                        userListService.userList(newUserName)
                            .success(function (data, status) {
                                $scope.users = data;
                            });
                    }, 350);
                }
            });
        }]);
    </script>
</body>
</html>

知识点:

$timeout 延时,防抖动

userListService 服务调用 $http 服务获取数据,返回一个对象

自定义 Service 命名不要以“$”开头,可以像内置 Service 一样注入的,注入时必须放在内置 Service 之后

Service 是单例的

Service 只要声明,不需要实例化

Service 在整个应用的生命周期中存在,可以用来共享数据


$filter

currency / date / filter / json / limitTo / lowercase / number / orderBy / uppercase

data 例:

{{ 1304375948024 | data }}

{{ 1304375948024 | data:"MM/dd/yyyy @ h:mma" }}

{{ 1304375948024 | data:"yyyy-MM-dd hh:mm:ss" }}

效果:

May 3, 2011

05/03/2011 @ 6:39AM

2011-05-03 06:39:08

自定义 filter:

<!doctype html>
<html ng-app="app">
<head>
    <meta charset="UTF-8">
    <script src="framework/1.3.0.14/angular.js"></script>
</head>
<body>
    {{'xoyozo'|filter1}}
    <script>
        var app = angular.module("app", []);
        app.filter('filter1', function () {
            return function (item) {
                return item + 'o(u_u)o';
            }
        });
    </script>
</body>
</html>

其它常用 Service:内置共 24 个

$compile 编译服务

$filter 数据格式化工具,内置了 8 个

$interval

$timeout

$locale

$location

$log

$parse

$http 封装了 ajax


2.6 综合实例 BookStore



xoyozo 7 年前
3,421

函数功能:添加/修改/删除/获取 URL 中的参数(锚点敏感)

/// <summary>
/// 设置 URL 参数(设 value 为 undefined 或 null 删除该参数)
/// <param name="url">URL</param>
/// <param name="key">参数名</param>
/// <param name="value">参数值</param>
/// </summary>
function fn_set_url_param(url, key, value) {
    // 第一步:分离锚点
    var anchor = "";
    var anchor_index = url.indexOf("#");

    if (anchor_index >= 0) {
        anchor = url.substr(anchor_index);
        url = url.substr(0, anchor_index);
    }

    // 第二步:删除参数
    key = encodeURIComponent(key);
    var patt;

    // &key=value
    patt = new RegExp("\\&" + key + "=[^&]*", "gi");
    url = url.replace(patt, "");

    // ?key=value&okey=value  -> ?okey=value
    patt = new RegExp("\\?" + key + "=[^&]*\\&", "gi");
    url = url.replace(patt, "?");

    // ?key=value
    patt = new RegExp("\\?" + key + "=[^&]*$", "gi");
    url = url.replace(patt, "");

    // 第三步:追加参数
    if (value != undefined && value != null) {
        value = encodeURIComponent(value);
        url += (url.indexOf("?") >= 0 ? "&" : "?") + key + "=" + value;
    }

    // 第四步:连接锚点
    url += anchor;

    return url;
}

/// <summary>
/// 获取 URL 参数(无此参数返回 null,多次匹配使用“,”连接)
/// <param name="url">URL</param>
/// <param name="key">参数名</param>
/// </summary>
function fn_get_url_param(url, key) {
    key = encodeURIComponent(key);
    var patt = new RegExp("[?&]" + key + "=([^&#]*)", "gi");

    var values = [];

    while ((result = patt.exec(url)) != null) {
        values.push(decodeURIComponent(result[1]));
    }

    if (values.length > 0) {
        return values.join(",");
    } else {
        return null;
    }
}


调用示例:

var url = window.location.href;

// 设置参数
url = fn_set_url_param(url, 'a', 123);

// 获取参数
console.log(fn_get_url_param, url, 'a');


压缩版:

function fn_set_url_param(b,c,e){var a="";var f=b.indexOf("#");if(f>=0){a=b.substr(f);b=b.substr(0,f)}c=encodeURIComponent(c);var d;d=new RegExp("\\&"+c+"=[^&]*","gi");b=b.replace(d,"");d=new RegExp("\\?"+c+"=[^&]*\\&","gi");b=b.replace(d,"?");d=new RegExp("\\?"+c+"=[^&]*$","gi");b=b.replace(d,"");if(e!=undefined&&e!=null){e=encodeURIComponent(e);b+=(b.indexOf("?")>=0?"&":"?")+c+"="+e}b+=a;return b}
function fn_get_url_param(b,c){c=encodeURIComponent(c);var d=new RegExp("[?&]"+c+"=([^&#]*)","gi");var a=[];while((result=d.exec(b))!=null){a.push(decodeURIComponent(result[1]))}if(a.length>0){return a.join(",")}else{return null}};


xoyozo 7 年前
3,487

本文记录 Newtonsoft.Json 的用法,System.Text.Json 请参此文

如何在序列化时间类型时以“年-月-日 时:分:秒”的格式输出?

默认时间类型将序列化为:2017-11-10T18:48:14.1685763+08:00,我们只要 new 一个 IsoDateTimeConverter 即可改变时间类型的输出格式:

var dtc = new IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-dd HH:mm:ss" };
string b = JsonConvert.SerializeObject(DateTime.Now, dtc);


如何将对象序列化为可友好阅读的字符串?

序列化后的字符串默认是“紧凑型”的,如:{"a":1,"b":2}

可以将 Formatting 指定为 Indented 即可输出格式化后的字符串,例:

var obj = new { a = 1, b = 2 };
string a = JsonConvert.SerializeObject(obj, Formatting.Indented);

结果如下:

{

  "a": 1,

  "b": 2

}


枚举类型输出为字符串,而非索引值

[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]

更多用法参:https://www.cnblogs.com/DomoYao/p/Json.html


不序列化属性

[Newtonsoft.Json.JsonIgnore]

当值为 null 时不序列化

[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]


不序列化值为 null 的项

var jSetting = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore };

JsonConvert.SerializeObject(obj, Formatting.Indented, jSetting);

xoyozo 7 年前
5,103

jquery.barrager.js 是一款优雅的网页弹幕插件,支持显示图片,文字以及超链接。支持速度、高度、颜色、数量等自定义。

使用方法参官方网站:http://yaseng.org/jquery.barrager.js/

jquery.barrager.min.js 会报错,改用 jquery.barrager.js(版本 1.1)

实战示例:https://xoyozo.net/Demo/DanMu

该示例重写了部分 CSS 样式。

注:info 属性支持 HTML,因此要自己过滤危险标签,建议仅保留 <img /> 标签

如果要显示在指定区域,可以使用 bottom 属性,例:

bottom: (Math.random() * (0.95 - 0.5) + 0.5) * $(window).height()

建议加 loading 锁,以适应网络不通畅的环境(ajax 时间超过定时间隔会导致请求到重复的数据)

跨域环境可以使用 JSONP 来实现,服务器端需设置 Access-Control-Allow-Origin 为 *。

xoyozo 7 年前
9,127

HTML5 Sortable 是一款使用原生 HTML5 drag 和 drop API 来对列表或网格进行排序的 jQuery 插件。


下载和使用:官网


步骤:

1、给 <ul /> 添加 sortable 样式类

2、引入 jquery.sortable.js

3、$('.sortable').sortable();


通过绑定 sortupdate 事件来获取排序后的 DOM 位置,示例:

$('.sortable').sortable().bind('sortupdate', function (event, ui) {
    var item = $(ui.item[0]);
    var prev = item.prev();

    $.post(window.location.pathname, {
        xdo: 'fn_sort',
        cid: item.data('cid'),
        prev: prev.data('cid') // 可能为 undefined
    }, function (json) {
        if (json.result.success) {
        } else {
            toastr['error'](json.result.info);
        }
    }, 'json');
});


xoyozo 7 年前
4,970

官方 Demo

WeUI:https://weui.io/

weui.js:https://weui.io/weui.js/


开发文档

WeUI:https://github.com/Tencent/weui/wiki

weui.js:https://github.com/Tencent/weui.js/blob/master/docs/README.md

WeUI for 小程序:https://github.com/Tencent/weui-wxss


引入

① <head /> 中加入:

<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=0" />

② 引用样式:(请更改地址中的版本号,参开发文档

<link href="https://res.wx.qq.com/t/wx_fed/weui-source/res/2.5.16/weui.min.css" rel="stylesheet" />

③ (可选)引用脚本:(请将下方链接中的版本号替换为最新)

<script src="https://res.wx.qq.com/t/wx_fed/weui.js/res/1.2.17/weui.min.js"></script>


经验

  • 搜索框的 <form /> 中不加 action 则键盘中显示灰色“换行”键,加入 action 则键盘中显示蓝色“搜索”键

xoyozo 8 年前
7,090
  1. 在 NuGet 中安装 Microsoft.AspNetCore.Session 和 Newtonsoft.Json

  2. 打开 Startup.cs,在 ConfigureServices 方法中加入(视情况配置相关属性)

    services.AddSession(options =>
    {
        // 设置了一个较短的时间,方便测试
        options.IdleTimeout = TimeSpan.FromSeconds(10);
        options.CookieHttpOnly = true;
    });
  3. 在 Configure 方法中加入

    app.UseSession();
  4. 添加 Extensions 文件夹并添加类 SessionExtensions.cs

    using Microsoft.AspNetCore.Http;
    using Newtonsoft.Json;
    
    public static class SessionExtensions
    {
        public static void Set<T>(this ISession session, string key, T value)
        {
            session.SetString(key, JsonConvert.SerializeObject(value));
        }
    
        public static T Get<T>(this ISession session, string key)
        {
            var value = session.GetString(key);
            return value == null ? default(T) :
                                  JsonConvert.DeserializeObject<T>(value);
        }
    }
  5. 使用方法

    HttpContext.Session.Set("abc", 123);
    var v1 = HttpContext.Session.Get<int>("abc");
    HttpContext.Session.Remove("abc");
    var v2 = HttpContext.Session.Get<int>("abc");
    // v1 = 123, v2 = 0
xoyozo 8 年前
4,848