如何使用Vite创建Vue3的uniapp项目 - CanotStop - 博客园

mikel阅读(336)

来源: 如何使用Vite创建Vue3的uniapp项目 – CanotStop – 博客园

项目结构

my-vue3-project
├─ .env //默认环境变量
├─ .env.development //开发环境变量
├─ .eslintrc-auto-import.json //(autoimport变量,eslint配置)由auto-import插件生成
├─ .eslintrc.js //eslint配置文件
├─ .gitignore
├─ auto-imports.d.ts //(autoimport变量,ts声明文件)由auto-import插件生成
├─ index.html
├─ jsconfig.json
├─ package-lock.json
├─ package.json
├─ src
│ ├─ App.vue
│ ├─ components
│ ├─ main.js
│ ├─ manifest.json //uniapp项目配置文件
│ ├─ pages
│ │ └─ index
│ │ └─ IndexView.vue
│ ├─ pages.json //页面配置文件
│ ├─ services // 请求后端服务目录
│ │ ├─ api.js // 后端api
│ │ └─ http.js //请求
│ ├─ shime-uni.d.ts
│ ├─ static //静态文件目录
│ │ └─ logo.png
│ ├─ store //pinia全局状态库
│ │ └─ useUserStore.js
│ ├─ uni.scss
│ └─ utils //公共的工具方法
└─ vite.config.js

创建历程

项目创建

npx degit dcloudio/uni-preset-vue#vite my-vue3-project
  • Vue3/Vite 版要求 node 版本^14.18.0 || >=16.0.0
  • 如果使用 HBuilderX(3.6.7 以下版本)运行 Vue3/Vite 创建的最新的 cli 工程,需要在 HBuilderX 运行配置最底部设置 node 路径 为自己本机高版本 node 路径(注意需要重启 HBuilderX 才可以生效)
  • HBuilderX Mac 版本菜单栏左上角 HBuilderX->偏好设置->运行配置->node 路径
  • HBuilderX Windows 版本菜单栏 工具->设置->运行配置->node 路径

项目依赖安装

Eslint

Eslint 的相关配置可参考文章:Web 项目如何配置 Eslint
注意:在配置完 Eslint 之后,在项目中使用 uni 的时候会报 uni 未被定义的错,需在.eslintrc.js 中加上以下代码

globals: {
uni: true;
}

vite.config.js 中 eslintPlugin 配置

import eslintPlugin from ‘vite-plugin-eslint’
export default defineConfig({
plugins: [
eslintPlugin({
cache: true,
include: [‘src/**/*.js’, ‘src/**/*.vue’, ‘src/*.js’, ‘src/*.vue’, ‘src/*.nvue’],
failOnError: false // eslint报错不影响运行
}),
]

Pinia

安装

注意:在这个项目中安装的 vue 版本是:3.2.47,pinia:3.1 及以上版本需要依赖 vue:3.3 及以上版本才能运行成功。

@REM npm 安装
npm i pinia@2.0
@REM node 安装
yarn add pinia@2.0

安装依赖时出现如下报错可尝试通过参考文章解决:npm 安装依赖时出现 Peer Dependencies 冲突报错

While resolving: uni-preset-vue@0.0.0
Found: vue@3.2.47
node_modules/vue
peer vue@”^3.2.25″ from @vitejs/plugin-vue@4.3.4
node_modules/@vitejs/plugin-vue
Conflicting peer dependency: vue@3.3.4
node_modules/vue
peer vue@”>= 2.5 < 2.7″ from @vue/composition-api@1.7.2
node_modules/@vue/composition-api
peerOptional @vue/composition-api@”^1.4.0″ from pinia@2.0.34
node_modules/pinia
pinia@”2.0.34″ from the root project
Fix the upstream dependency conflict, or retry
this command with –force or –legacy-peer-deps
to accept an incorrect (and potentially broken) dependency resolution.

Luch-Request

uniapp 原生的 uni.request 使用比较麻烦,它并非使用 Promise 的形式也不支持请求拦截和相应拦截的配置,而 luch-request 基于 Promise 对象实现更简单的 request 使用方式,支持请求和响应拦截

  • 支持全局挂载
  • 支持多个全局配置实例
  • 支持自定义验证器
  • 支持文件上传/下载
  • 支持 task 操作
  • 支持自定义参数
  • 支持多拦截器
  • 对参数的处理比 uni.request 更强

安装

npm install luch-request -S

Sass 支持

安装

npm i sass sass-loader -D

unplugin-auto-import

unplugin-auto-import 是为 Vite、Webpack、Rollup 和 esbuild 按需自动导入 API,同时支持 TypeScript。
使用它在 vue3 项目中使用预设导入的 api 就不需要 import,可以通过预设自动导入模块,增强开发体验

安装

npm i -D unplugin-auto-import

在 Vite.config.js 中加入如下配置

import AutoImport from “unplugin-auto-import/vite”;
export default defineConfig({
plugins: [
AutoImport({
// 需要自动引入的文件
include: [
/\.[j]sx?$/,
/\.vue$/,
/\.vue\?vue/,
/\.nvue$/,
/\.nvue\?nvue/,
/\.md$/,
],
// 全局自动引入api预设
imports: [
// 插件预设支持导入的api
“vue”,
// ‘vue-router’,
“pinia”,
“uni-app”,
// 自定义导入的api
],
// eslint配置
eslintrc: {
enabled: true, // Default `false`
filepath: “./.eslintrc-auto-import.json”, // Default `./.eslintrc-auto-import.json`
globalsPropValue: true, // Default `true`, (true | false | ‘readonly’ | ‘readable’ | ‘writable’ | ‘writeable’)
},
dts: “./auto-imports.d.ts”,
// dts:false
}),
],
});

生成 eslint 配置文件并使用

通过执行命令npm run dev:h5 运行项目,运行成功时项目根目录会生成auto-imports.d.ts.eslintrc-auto-import.json两个文件,然后在.eslintrc.js 文件中的 extends 属性中,引入该文件

extends: [
‘standard’,
‘plugin:vue/vue3-essential’,
‘./.eslintrc-auto-import.json’
]

注意:引入后需重新运行项目才能生效

添加默认和开发环境变量文件

在项目根目录添加.env 和.env.development 文件,以配置环境变量
详细配置教程可参照官网:Vite 环境变量和模式

配置 UI 库 uni-ui

安装

npm i @dcloudio/uni-ui

配置

vite.config.js

注意 cli 项目默认是不编译 node_modules 下的组件的,导致条件编译等功能失效 ,导致组件异常 需要在 vite.config.js 增加 @dcloudio/uni-ui 包的编译即可正常

transpileDependencies: [“@dcloudio/uni-ui”];

pages.json

正常来说这样子引入的话,使用组件时,需要在页面处 import 才可以使用,然而可以通过 npm+easycom 的形式来全局引入组件。在 pages.json 中加入如下代码,即可在使用组件而不需在页面处 import。

“easycom”: {
“autoscan”: true,
“custom”: {
“^uni-(.*)”: “@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue”
}
}

项目到这里就结束了,这里提供该项目的 github 地址,方便拉取直接使用

这个项目中 master 分支中使用的 Eslint 风格为 Standard,而 prettier 分支使用的风格为 Prettier

https://github.com/luxigaola/my-uniapp-project

  1. 内容来自 uniapp 官网,详情可查看使用 vue-cli 查看目录 ↩︎
  2. pinia 官网 ↩︎
  3. luch-request 官网 ↩︎
  4. sass 官网 ↩︎
  5. unplugin-auto-import 官网 ↩︎
  6. uni-ui Github 地址 ↩︎

10款Visual Studio实用插件 - 追逐时光者 - 博客园

mikel阅读(384)

来源: 10款Visual Studio实用插件 – 追逐时光者 – 博客园

10款Visual Studio实用插件 

前言

俗话说的好工欲善其事必先利其器,安装一些Visual Studio实用插件对自己日常的开发和工作效率能够大大的提升,避免996从选一款好的IDE实用插件开始。以下是我认为比较实用的Visual Studio插件希望对大家有用,大家有更好的插件推荐可在文末留言🤞。

Visual Studio插件搜索

扩展=>管理扩展:

Visual Studio插件市场

https://marketplace.visualstudio.com/👉

ReSharper(付费)

ReSharper 是一个用于 Visual Studio 的强大插件,它为.NET开发者提供了许多功能,以提高编码效率、代码质量和开发体验。旨在帮助.NET开发者编写高质量、高效的代码,并提供了许多辅助工具来改进开发流程和减少常见的编码错误。(注意电脑内存小的同学慎用,比较占用内存)

插件市场地址:https://marketplace.visualstudio.com/items?itemName=JetBrains.ReSharper👉

GitHub Copilot(付费)

GitHub Copilot 是一款AI辅助工具,帮助开发者更快速、智能地编写高质量代码。

插件市场地址:https://marketplace.visualstudio.com/items?itemName=GitHub.copilotvs👉

CodeMaid(免费)

CodeMaid 是 Visual Studio 的开源扩展,用于清理和简化 C#、C++、F#、VB、PHP、PowerShell、R、JSON、XAML、XML、ASP、HTML、CSS、LESS、SCSS、JavaScript 和 TypeScript 编码。

插件市场地址:https://marketplace.visualstudio.com/items?itemName=SteveCadwallader.CodeMaidVS2022👉

CSharpier(免费)

CSharpier 是一个用于 C# 代码的代码格式化工具。它是一个基于 Roslyn 的工具,它会解析您的 C# 代码,然后根据特定的规则重新格式化它,使其符合 CSharpier 的代码风格。这个工具的设计目的是使代码风格保持一致,从而提高代码的可读性和可维护性。

插件市场地址:https://marketplace.visualstudio.com/items?itemName=csharpier.CSharpier#report-abuse👉

格式之前:

格式之后:

Visual Studio Theme Pack(免费)

Visual Studio流行主题集。

插件市场地址:https://marketplace.visualstudio.com/items?itemName=idex.vsthemepack👉

Indent Guides(免费)

显示代码缩进线,有助于保持代码的结构清晰。页宽标记有三种样式:实线、点线面和虚线,有粗细之分,颜色也可自定义。默认为灰色虚线,如图所示。每个缩进级别可以有不同的样式和颜色。

插件市场地址:https://marketplace.visualstudio.com/items?itemName=SteveDowerMSFT.IndentGuides2022👉

Markdown Editor(免费)

功能齐全的 Markdown 编辑器,具有实时预览和语法高亮功能。支持 GitHub 版本的 Markdown。

插件市场地址:https://marketplace.visualstudio.com/items?itemName=MadsKristensen.MarkdownEditor👉

HTML Snippet Pack(免费)

它提供了一组预定义的 HTML 代码段(snippets),以帮助开发者更快速地编写 HTML 代码。这些代码段是预先定义的代码模板,可以通过简单的代码缩写触发,然后自动生成相应的 HTML 代码块,从而提高编写 HTML 的效率和准确性。

插件市场地址:https://marketplace.visualstudio.com/items?itemName=MadsKristensen.HTMLSnippetPack👉

Ozcode(免费)

Ozcode 是一个用于 Visual Studio 的插件拓展,旨在优化和简化 C# 调试过程。它提供了一系列功能,可以帮助开发人员更快速地检测和解决代码中的错误,从而提高调试效率和开发生产力。这个插件拓展使调试变得更加直观和高效,有助于加速软件开发周期。

插件市场地址:https://marketplace.visualstudio.com/items?itemName=CodeValueLtd.OzCode👉

Live Share(免费)

它的主要功能是支持实时协作开发,并让开发人员能够在他们最喜欢的开发工具中进行协作。

插件市场地址:https://marketplace.visualstudio.com/items?itemName=MS-vsliveshare.vsls-vs👉

webapi开发框架实践 - 星仔007 - 博客园

mikel阅读(331)

来源: webapi开发框架实践 – 星仔007 – 博客园

项目链接以及目录结构

liuzhixin405/efcore-template (github.com)

这是一个纯webapi的开发框架。

1、支持的orm有efcore6、dapper,可以灵活切换数据库。

复制代码
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Elfie.Model;
using Microsoft.EntityFrameworkCore;
using project.Context;
using project.Repositories;
using project.Services;
using RepositoryComponent.DbFactories;

namespace project.Extensions
{
    public static partial class TheExtensions
    {
        public static void AddDatabase(this WebApplicationBuilder builder)
        {
            ///sqlserver   
            if (builder.Configuration["DbType"]?.ToLower() == "sqlserver")
            {
                builder.Services.AddDbContext<ReadProductDbContext>(options => options.UseSqlServer(builder.Configuration["ConnectionStrings:SqlServer:ReadConnection"]), ServiceLifetime.Scoped);
                builder.Services.AddDbContext<WriteProductDbContext>(options => options.UseSqlServer(builder.Configuration["ConnectionStrings:SqlServer:WriteConnection"]), ServiceLifetime.Scoped);

            }
            ///mysql
            else if (builder.Configuration["DbType"]?.ToLower() == "mysql")
            {
                builder.Services.AddDbContext<ReadProductDbContext>(options => options.UseMySQL(builder.Configuration["ConnectionStrings:MySql:ReadConnection"]), ServiceLifetime.Scoped);
                builder.Services.AddDbContext<WriteProductDbContext>(options => options.UseMySQL(builder.Configuration["ConnectionStrings:MySql:WriteConnection"]), ServiceLifetime.Scoped);

            }
            else
            {
                //throw new ArgumentNullException("δ����ȷ��ע�����ݿ�");
                builder.Services.AddDbContext<ReadProductDbContext>(options => options.UseInMemoryDatabase("test_inmemory_db"), ServiceLifetime.Scoped);
                builder.Services.AddDbContext<WriteProductDbContext>(options => options.UseInMemoryDatabase("test_inmemory_db"), ServiceLifetime.Scoped);

            }

            builder.Services.AddScoped<Func<ReadProductDbContext>>(provider => () => provider.GetService<ReadProductDbContext>() ?? throw new ArgumentNullException("ReadProductDbContext is not inject to program"));
            builder.Services.AddScoped<Func<WriteProductDbContext>>(provider => () => provider.GetService<WriteProductDbContext>() ?? throw new ArgumentNullException("WriteProductDbContext is not inject to program"));

            builder.Services.AddScoped<DbFactory<WriteProductDbContext>>();
            builder.Services.AddScoped<DbFactory<ReadProductDbContext>>();

            builder.Services.AddTransient<IReadProductRepository, ProductReadRepository>();
            builder.Services.AddTransient<IWriteProductRepository, ProductWriteRepository>();
            builder.Services.AddTransient<IProductService, ProductService>();

            builder.Services.AddTransient<ICustomerService, CustomerService>();
        }
    }
}
复制代码

2、至于消息中间件有rabbitmq、kafka,也是通过配置文件来指定哪一个实现。

复制代码
using MessageMiddleware.Factory;
using MessageMiddleware.RabbitMQ;

namespace project.Extensions
{
    public static partial class TheExtensions
    {
        public static void AddMq(this WebApplicationBuilder builder)
        {
            var rabbitMqSetting = new RabbitMQSetting
            {
                ConnectionString = builder.Configuration["MqSetting:RabbitMq:ConnectionString"].Split(';'),
                Password = builder.Configuration["MqSetting:RabbitMq:PassWord"],
                Port = int.Parse(builder.Configuration["MqSetting:RabbitMq:Port"]),
                SslEnabled = bool.Parse(builder.Configuration["MqSetting:RabbitMq:SslEnabled"]),
                UserName = builder.Configuration["MqSetting:RabbitMq:UserName"],
            };
            var kafkaSetting = new MessageMiddleware.Kafka.Producers.ProducerOptions
            {
                BootstrapServers = builder.Configuration["MqSetting:Kafka:BootstrapServers"],
                SaslUsername = builder.Configuration["MqSetting:Kafka:SaslUserName"],
                SaslPassword = builder.Configuration["MqSetting:Kafka:SaslPassWord"],
                Key = builder.Configuration["MqSetting:Kafka:Key"]
            };
            var mqConfig = new MQConfig
            {
                ConsumerLog = bool.Parse(builder.Configuration["MqSetting:ConsumerLog"]),
                PublishLog = bool.Parse(builder.Configuration["MqSetting:PublishLog"]),
                Rabbit = rabbitMqSetting,
                Use = int.Parse(builder.Configuration["MqSetting:Use"]),
                Kafka = kafkaSetting
            };
            builder.Services.AddSingleton<MQConfig>(sp => mqConfig);
            builder.Services.AddMQ(mqConfig);
        }
    }
}
复制代码

3、该项目还集成了mongodb和elasticsearch,在project项目中没有写实现案例,实现起来也很简单。

4、下面是分布式雪花id的实现,先注入代码,使用的时候直接使用distributedid即可。

复制代码
 builder.Services.AddDistributedLock(x =>
 {
     x.LockType = LockType.InMemory;
     x.RedisEndPoints = new string[] { builder.Configuration["DistributedRedis:ConnectionString"] ?? throw new Exception("$未能获取distributedredis连接字符串")};
 }).AddCache(new CacheOptions
 {
     CacheType = CacheTypes.Redis,
     RedisConnectionString = builder.Configuration["DistributedRedis:ConnectionString"] ?? throw new Exception("$未能获取distributedredis连接字符串")
 }).AddDistributedId(new DistributedIdOptions
 {
     Distributed = true
 });
复制代码
1
newProduct.Id = _distributedId.NewLongId().ToString();

5、缓存使用的是分布式缓存和内存缓存,其中分布式缓存有一般实现和指定序列化格式的实现。

复制代码
using System.Text;
using System.Text.Json.Serialization;
using MessagePack;
using StackExchange.Redis.Extensions.Core;
using StackExchange.Redis.Extensions.Core.Abstractions;
using StackExchange.Redis.Extensions.Core.Configuration;
using StackExchange.Redis.Extensions.Core.Implementations;

namespace project.Utility.Helper
{
    public class CacheHelper
    {
        private static IRedisClientFactory _factory_with_msgpack;
        private static IRedisDatabase _redis_with_msgpack => _factory_with_msgpack.GetDefaultRedisDatabase();

        private static IRedisClientFactory _factory;
        private static IRedisDatabase _redis => _factory.GetDefaultRedisDatabase();
        public static void Init(IConfiguration configuration)
        {
            var config = configuration.GetSection("Redis").Get<RedisConfiguration>();
            _factory = new RedisClientFactory(new[] { config }, null, new RedisSerializer());
            _factory_with_msgpack = new RedisClientFactory(new[] { config }, null, new RedisMessagepackSerializer());
        }
        static CacheHelper() { }

        public static T Get<T>(string key)
        {
            return _redis.GetAsync<T>(key).GetAwaiter().GetResult();
        }
        public static async Task<T> GetAsync<T>(string key)
        {
            return await _redis.GetAsync<T>(key);
        }
        public static async Task<T> GetAsync_With_Msgpack<T>(string key)
        {
            return await _redis_with_msgpack.GetAsync<T>(key);
        }

        public static string Get(string key)
        {
            return _redis.GetAsync<string>(key).GetAwaiter().GetResult();
        }

        public static bool Set(string key, object value, TimeSpan expiresIn)
        {
            return _redis.AddAsync(key, value, expiresIn).GetAwaiter().GetResult();
        }
        public static async Task<bool> SetAsync(string key, object value, TimeSpan expiresIn)
        {
            return await _redis.AddAsync(key, value, expiresIn);
        }

        public static async Task<bool> SetAsync_With_Msgpack(string key, object value, TimeSpan expiresIn)
        {
            return await _redis_with_msgpack.AddAsync(key, value, expiresIn);
        }

        /// <summary>
        /// 以秒为单位,返回给定 key 的剩余生存时间
        /// </summary>

        public static long GetExpirin(string key)
        {
            var timespan = _redis.Database.KeyTimeToLive(key);
            if (timespan == null) { return 0; }
            return (long)timespan.Value.TotalSeconds;
        }
        public static bool KeyExpire(string key, TimeSpan expiresIn)
        {
            return _redis.Database.KeyExpire(key, expiresIn);
        }
        public static async Task<bool> RemoveKeyAsync(string key)
        {
            return await _redis.Database.KeyDeleteAsync(key);
        }
        public static long RemoveKey(string key)
        {
            var result = _redis.Database.KeyDelete(key);
            return result ? 1 : 0;
        }
    }


    public class RedisSerializer : ISerializer
    {
        public T? Deserialize<T>(byte[] serializedObject)
        {
            var data = Encoding.UTF8.GetString(serializedObject);
            return System.Text.Json.JsonSerializer.Deserialize<T>(data);
        }

        public byte[] Serialize<T>(T? item)
        {
            var data = System.Text.Json.JsonSerializer.Serialize(item);
            return Encoding.UTF8.GetBytes(data);
        }
    }

    public class RedisMessagepackSerializer : ISerializer
    {
        private MessagePackSerializerOptions _options;
        public RedisMessagepackSerializer()
        {
            _options = MessagePackSerializerOptions.Standard.WithCompression(MessagePackCompression.Lz4BlockArray);
        }
        public T? Deserialize<T>(byte[] serializedObject)
        {
            return MessagePackSerializer.Deserialize<T>(serializedObject, _options);
        }

        public byte[] Serialize<T>(T? item)
        {
            return MessagePackSerializer.Serialize(item, _options);
        }
    }
}
复制代码

6、单元测试、集成测试没有写。

更细节的需要自己看代码,这应该是一个基本的开发具备的功能。

该项目下载下来可以直接运行。

阿里云图生视频工具:I2VGen-XL - image to video - AIBetas

mikel阅读(909)

来源: 阿里云图生视频工具:I2VGen-XL – image to video – AIBetas

2VGen-XL简介

I2VGen-XL项目由阿里云达摩院研发,用于根据输入图像生成高清视频。模型经大规模视频和图像数据混合预训练,并在少量精品数据上微调,具有良好的泛化性。相比现有的视频生成模型,I2VGen-XL在清晰度、质感、语义、时序连续性等方面具有优势。

  • [💡] I2VGen-XL模型的核心部分包含两个阶段,分别解决语义一致性和清晰度的问题,参数量共计约37亿。
  • [👍] I2VGen-XL模型在大规模视频和图像数据混合预训练,并在少量精品数据上微调,具有良好的泛化性。
  • [🎯] 相比现有的视频生成模型,I2VGen-XL在清晰度、质感、语义、时序连续性等方面具有优势。
  • [🎬] I2VGen-XL模型可以直接生成720P分辨率的视频,无水印,适用更多视频平台,减少许多限制。
  • [🔧] I2VGen-XL模型在处理小目标生成、快速运动目标生成以及生成速度等方面存在局限性。
  • [📚] I2VGen-XL模型的训练数据来源广泛,类别分布广,数据来源于开源数据、视频网站以及其他内部数据,具有多分辨率、长宽比等。

2VGen-XL生成效果

图片[1] - 阿里云图生视频工具:I2VGen-XL - image to video - AIBetas
图片[2] - 阿里云图生视频工具:I2VGen-XL - image to video - AIBetas

2VGen-XL模型体验

Demo:https://modelscope.cn/studios/damo/I2VGen-XL-Demo/summary

图片[3] - 阿里云图生视频工具:I2VGen-XL - image to video - AIBetas

2VGen-XL项目

Paper: https://arxiv.org/abs/2306.02018

项目开源地址:https://modelscope.cn/models/damo/Image-to-Video/summary

Github地址:https://www.github.com/camenduru/I2VGen-XL-colab

.NET开源最全的第三方登录整合库 - CollectiveOAuth - 追逐时光者 - 博客园

mikel阅读(329)

来源: .NET开源最全的第三方登录整合库 – CollectiveOAuth – 追逐时光者 – 博客园

前言

我相信很多同学都对接过各种各样的第三方平台的登录授权获取用户信息(如:微信登录、支付宝登录、GitHub登录等等)。今天给大家推荐一个.NET开源最全的第三方登录整合库:CollectiveOAuth。

官方项目介绍

.Net平台(C#) 史上最全的整合第三方登录的开源库 => 环境支持 .NET Framework 4.5 ~ 4.6.2 和 .NetCore 3.1。目前已包含Github、Gitee、钉钉、百度、支付宝、微信、企业微信、腾讯云开发者平台(Coding)、OSChina、微博、QQ、Google、Facebook、抖音、领英、小米、微软、今日头条、Teambition、StackOverflow、Pinterest、人人、华为、酷家乐、Gitlab、美团、饿了么、等第三方平台的授权登录。

项目特点

  • 全:已集成二十多家第三方平台(国内外常用的基本都已包含),仍然还在持续扩展中!
  • 简:API就是奔着最简单去设计的(见后面快速开始),尽量让您用起来没有障碍感!

企业微信扫码授权快速开始

0、企业微信开发对接文档

文档介绍

企业微信扫码授权登录官方文档地址:https://developer.work.weixin.qq.com/document/path/91025,在进行企业微信扫码授权绑定/登录之前需要先自建应用,同时需要开启网页授权登录,具体自建应用的相关操作可以参考博文:https://developer.aliyun.com/article/1136114

管理平台接入

完成了上面企业微信管理后台的相关配置之后,我们就可以按照文档步骤开始操作了

构造二维码

关于构造企业微信扫码绑定/登录二维码一共有两种方式:构造独立窗口登录二维码、构造内嵌登录二维码,下面简单说一下构造独立窗口登录二维码

构造独立窗口登录二维码

构造独立窗口登录二维码,可以在页面放置一个 button 按钮,添加点击事件,在触发点击事件时访问连接https://open.work.weixin.qq.com/wwopen/sso/qrConnect?appid=CORPID&agentid=AGENTID&redirect_uri=REDIRECT_URI&state=STATE,效果如图:

连接参数说明:

 

1、引入依赖

下载源码,添加Come.CollectiveOAuth类库。

 

2、配置企业微信扫码授权(appsettings.json)中配置

{
  "AppSettings": {
    //企业微信扫码授权
    "CollectiveOAuth_WECHAT_ENTERPRISE_SCAN_ClientId": "xxxxxxxxxxxxxxxxx",
    "CollectiveOAuth_WECHAT_ENTERPRISE_SCAN_AgentId""xxxxxx",
    "CollectiveOAuth_WECHAT_ENTERPRISE_SCAN_ClientSecret""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "CollectiveOAuth_WECHAT_ENTERPRISE_SCAN_RedirectUri""https://yours.domain.com/oauth2/callback?authSource=WECHAT_ENTERPRISE_SCAN"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft""Warning",
      "Microsoft.Hosting.Lifetime""Information"
    }
  },
  "AllowedHosts": "*"
}

构建授权Url方法

        /// <summary>
        /// 构建授权Url方法
        /// </summary>
        /// <param name="authSource"></param>
        /// <returns>RedirectUrl</returns>
        public IActionResult Authorization(string authSource= "WECHAT_ENTERPRISE_SCAN")
        {
            AuthRequestFactory authRequest = new AuthRequestFactory();
            var request = authRequest.getRequest(authSource);
            var authorize = request.authorize(AuthStateUtils.createState());
            Console.WriteLine(authorize);
            return Redirect(authorize);
        }
    public class AuthRequestFactory
    {

        #region 从Webconfig中获取默认配置(可以改造成从数据库中读取)
        public Dictionary<string, ClientConfig> _clientConfigs;

        public Dictionary<string, ClientConfig> ClientConfigs
        {
            get
            {
                if (_clientConfigs == null)
                {
                    var _defaultPrefix = "CollectiveOAuth_";
                    _clientConfigs = new Dictionary<string, ClientConfig>();

                    #region 或者默认授权列表数据
                    var defaultAuthList = typeof(DefaultAuthSourceEnum).ToList().Select(a => a.Name.ToUpper()).ToList();
                    foreach (var authSource in defaultAuthList)
                    {
                        var clientConfig = new ClientConfig();
                        clientConfig.clientId = AppSettingUtils.GetStrValue($"{_defaultPrefix}{authSource}_ClientId");
                        clientConfig.clientSecret = AppSettingUtils.GetStrValue($"{_defaultPrefix}{authSource}_ClientSecret");
                        clientConfig.redirectUri = AppSettingUtils.GetStrValue($"{_defaultPrefix}{authSource}_RedirectUri");
                        clientConfig.alipayPublicKey = AppSettingUtils.GetStrValue($"{_defaultPrefix}{authSource}_AlipayPublicKey");
                        clientConfig.unionId = AppSettingUtils.GetStrValue($"{_defaultPrefix}{authSource}_UnionId");
                        clientConfig.stackOverflowKey = AppSettingUtils.GetStrValue($"{_defaultPrefix}{authSource}_StackOverflowKey");
                        clientConfig.agentId = AppSettingUtils.GetStrValue($"{_defaultPrefix}{authSource}_AgentId");
                        clientConfig.scope = AppSettingUtils.GetStrValue($"{_defaultPrefix}{authSource}_Scope");
                        _clientConfigs.Add(authSource, clientConfig);
                    }
                    #endregion
                }
                return _clientConfigs;
            }
        }


        public ClientConfig GetClientConfig(string authSource)
        {
            if (authSource.IsNullOrWhiteSpace())
            {
                return null;
            }

            if (!ClientConfigs.ContainsKey(authSource))
            {
                return null;
            }
            else
            {
                return ClientConfigs[authSource];
            }
        } 
        #endregion

        /**
        * 返回AuthRequest对象
        *
        * @return {@link AuthRequest}
        */
        public IAuthRequest getRequest(string authSource)
        {
            // 获取 CollectiveOAuth 中已存在的
            IAuthRequest authRequest = getDefaultRequest(authSource);
            return authRequest;
        }


        /// <summary>
        /// 获取默认的 Request
        /// </summary>
        /// <param name="authSource"></param>
        /// <returns>{@link AuthRequest}</returns>
        private IAuthRequest getDefaultRequest(string authSource)
        {
            ClientConfig clientConfig = GetClientConfig(authSource);
            IAuthStateCache authStateCache = new DefaultAuthStateCache();

            DefaultAuthSourceEnum authSourceEnum = GlobalAuthUtil.enumFromString<DefaultAuthSourceEnum>(authSource);

            switch (authSourceEnum)
            {

                case DefaultAuthSourceEnum.WECHAT_MP:
                    return new WeChatMpAuthRequest(clientConfig, authStateCache);

                case DefaultAuthSourceEnum.WECHAT_OPEN:
                    return new WeChatOpenAuthRequest(clientConfig, authStateCache);

                case DefaultAuthSourceEnum.WECHAT_ENTERPRISE:
                    return new WeChatEnterpriseAuthRequest(clientConfig, authStateCache);

                case DefaultAuthSourceEnum.WECHAT_ENTERPRISE_SCAN:
                    return new WeChatEnterpriseScanAuthRequest(clientConfig, authStateCache);

                case DefaultAuthSourceEnum.ALIPAY_MP:
                    return new AlipayMpAuthRequest(clientConfig, authStateCache);

                case DefaultAuthSourceEnum.GITEE:
                    return new GiteeAuthRequest(clientConfig, authStateCache);

                case DefaultAuthSourceEnum.GITHUB:
                    return new GithubAuthRequest(clientConfig, authStateCache);

                case DefaultAuthSourceEnum.BAIDU:
                    return new BaiduAuthRequest(clientConfig, authStateCache);

                case DefaultAuthSourceEnum.XIAOMI:
                    return new XiaoMiAuthRequest(clientConfig, authStateCache);

                case DefaultAuthSourceEnum.DINGTALK_SCAN:
                    return new DingTalkScanAuthRequest(clientConfig, authStateCache);

                case DefaultAuthSourceEnum.OSCHINA:
                    return new OschinaAuthRequest(clientConfig, authStateCache);

                case DefaultAuthSourceEnum.CODING:
                    return new CodingAuthRequest(clientConfig, authStateCache);

                case DefaultAuthSourceEnum.LINKEDIN:
                    return new LinkedInAuthRequest(clientConfig, authStateCache);

                case DefaultAuthSourceEnum.WEIBO:
                    return new WeiboAuthRequest(clientConfig, authStateCache);

                case DefaultAuthSourceEnum.QQ:
                    return new QQAuthRequest(clientConfig, authStateCache);

                case DefaultAuthSourceEnum.DOUYIN:
                    return new DouyinAuthRequest(clientConfig, authStateCache);

                case DefaultAuthSourceEnum.GOOGLE:
                    return new GoogleAuthRequest(clientConfig, authStateCache);

                case DefaultAuthSourceEnum.FACEBOOK:
                    return new FackbookAuthRequest(clientConfig, authStateCache);

                case DefaultAuthSourceEnum.MICROSOFT:
                    return new MicrosoftAuthRequest(clientConfig, authStateCache);

                case DefaultAuthSourceEnum.TOUTIAO:
                    return new ToutiaoAuthRequest(clientConfig, authStateCache);

                case DefaultAuthSourceEnum.TEAMBITION:
                    return new TeambitionAuthRequest(clientConfig, authStateCache);

                case DefaultAuthSourceEnum.RENREN:
                    return new RenrenAuthRequest(clientConfig, authStateCache);

                case DefaultAuthSourceEnum.PINTEREST:
                    return new PinterestAuthRequest(clientConfig, authStateCache);

                case DefaultAuthSourceEnum.STACK_OVERFLOW:
                    return new StackOverflowAuthRequest(clientConfig, authStateCache);

                case DefaultAuthSourceEnum.HUAWEI:
                    return new HuaweiAuthRequest(clientConfig, authStateCache);

                case DefaultAuthSourceEnum.KUJIALE:
                    return new KujialeAuthRequest(clientConfig, authStateCache);

                case DefaultAuthSourceEnum.GITLAB:
                    return new GitlabAuthRequest(clientConfig, authStateCache);

                case DefaultAuthSourceEnum.MEITUAN:
                    return new MeituanAuthRequest(clientConfig, authStateCache);

                case DefaultAuthSourceEnum.ELEME:
                    return new ElemeAuthRequest(clientConfig, authStateCache);

                default:
                    return null;
            }
        }
    }

API列表

https://gitee.com/rthinking/CollectiveOAuth#api%E5%88%97%E8%A1%A8

 

项目源码地址

更多实用功能特性欢迎前往开源地址查看👀,别忘了给项目一个Star💖。

https://gitee.com/rthinking/CollectiveOAuth

优秀项目和框架精选

该项目已收录到C#/.NET/.NET Core优秀项目和框架精选中,关注优秀项目和框架精选能让你及时了解C#、.NET和.NET Core领域的最新动态和最佳实践,提高开发效率和质量。坑已挖,欢迎大家踊跃提交PR,自荐(让优秀的项目和框架不被埋没🤞)。

https://github.com/YSGStudyHards/DotNetGuide/blob/main/docs/DotNet/DotNetProjectPicks.md

加入DotNetGuide技术交流群

  1. 提供.NET开发者分享自己优质文章的群组和获取更多全面的C#/.NET/.NET Core学习资料、视频、文章、书籍,社区组织,工具和常见面试题资源,帮助大家更好地了解和使用 .NET技术。
  2. 在这个群里,开发者们可以分享自己的项目经验、遇到的问题以及解决方案,倾听他人的意见和建议,共同成长与进步。
  3. 可以结识更多志同道合的开发者,甚至可能与其他开发者合作完成有趣的项目。通过这个群组,我们希望能够搭建一个积极向上、和谐友善的.NET技术交流平台,为广大.NET开发者带来更多的价值。

C# 怎么用OpenCVSharp4实现图片表格识别

mikel阅读(877)

要使用OpenCVSharp4来实现图片表格识别,你可以按照以下步骤进行操作:

安装OpenCVSharp4:首先,你需要安装OpenCVSharp4库。你可以通过NuGet包管理器或者从OpenCVSharp的官方网站下载并安装它。

导入必要的命名空间:在你的C#项目中,导入OpenCVSharp4的命名空间,以便能够使用它的功能。

using OpenCvSharp;
using OpenCvSharp.Extensions;

读取图片:使用OpenCVSharp4加载你要进行表格识别的图片。

Mat image = Cv2.ImRead(“your_image_path.jpg”);

图像预处理:在进行表格识别之前,你可能需要对图像进行一些预处理,以增强表格的检测和识别。这包括灰度化、二值化、去噪等操作,具体取决于你的图像特点。

Mat grayImage = new Mat();
Cv2.CvtColor(image, grayImage, ColorConversionCodes.BGR2GRAY);
Cv2.Threshold(grayImage, grayImage, 0, 255, ThresholdTypes.Binary);

表格检测:使用OpenCVSharp4的功能来检测图像中的表格。这可以通过查找图像中的直线、边缘或者轮廓来实现。

HoughLinesP(grayImage, out LineSegmentPoint[] lines, 1, Math.PI / 180, 100, 100, 10);

表格识别:一旦你检测到了表格的线条,你可以使用这些线条来提取表格中的内容。你可以根据线条的位置来切割图像,然后进一步处理每个表格单元格中的内容。

请注意,图像表格识别是一个复杂的任务,它可能需要一些调试和参数调整,以适应不同的图像和表格样式。你可能还需要使用OCR(光学字符识别)工具来提取单元格中的文本信息。

这只是一个基本的示例,实际的实现可能需要更多的步骤和细节。你可能需要深入研究OpenCVSharp4的文档以获取更多关于表格检测和图像处理的信息。

当你已经完成了表格检测并切割了图像以获取表格单元格的区域后,接下来的步骤通常包括:

单元格内容识别:对于每个切割出的表格单元格区域,你可以使用OCR工具来识别文本内容。Tesseract是一个常用的OCR引擎,你可以将其集成到你的C#项目中。以下是一个使用Tesseract的示例:

using Tesseract;

using (var engine = new TesseractEngine(@”tessdataPath”, “eng”, EngineMode.Default))
{
using (var img = Pix.LoadFromFile(“cell_image.png”))
{
using (var page = engine.Process(img))
{
string text = page.GetText();
// 处理识别出的文本内容
}
}
}

这里,tessdataPath应该是Tesseract数据文件的路径,”eng”表示使用英语语言模型,你可以根据需要选择其他语言模型。

处理识别结果:一旦你获取了每个表格单元格的识别文本,你可以根据需要对文本进行进一步的处理,如数据清理、格式化或存储。

结果输出:最后,你可以将识别出的表格数据输出到所需的格式,如Excel、CSV等,或者将其显示在用户界面中

请注意,表格识别是一个具有挑战性的任务,特别是对于复杂的表格和不同的图像质量。你可能需要调整和优化算法、参数和图像预处理步骤,以确保准确性和性能。另外,对于不同的语言和文本字体,OCR的准确性也可能会有所不同。

最好的方式是根据你的具体需求和数据集进行实验和测试,以找到最适合你的表格识别方法。同时,记得保持代码的可维护性和可扩展性,以便随着项目的发展进行进一步的改进和优化。

C#结合OpenCVSharp4图片相似度识别 - 宣君 - 博客园

mikel阅读(1131)

OpenCVSharp4

来源: C#结合OpenCVSharp4图片相似度识别 – 宣君 – 博客园

OpenCVSharp4图片相似度识别

需求背景:需要计算两个图片的相似度,然后将相似的图片进行归纳

1. 图片相似度算法

由于我是CRUD后端仔,对图像处理没什么概念。因此网上调研了几种相似度算法分析其适用场景。

直方图算法

获取要比较的2个图片的直方图数据,然后再将直方图数据归一化比较,最终得到一个相似指数,通过设定相似指数的边界,以此判断是否相同图片。

平均值哈希算法 aHash

转灰度压缩之后计算均值,最终通过像素比较得出哈希值,速度很快,但敏感度很高,稍有变化就会极大影响判定结果,精准度较差。因此比较适用于缩略图比较,最常用的就是以图搜图

感知哈希算法 pHash

在均值哈希基础上加入DCT(离散余弦变化),两次DCT就可以很好的将图像按照频度分开,取左上角高能低频信息做均值哈希,因此,精确度很高,但是速度方面较差一些。相比较aHashpHash更加适合用于缩略图比较,也非常适合比较两个近似图片是否相等。

差异值哈希算法 dHash

灰度压缩之后,比较相邻像素之间差异。假设有10×10的图像,每行10个像素,就会产生9个差异值,一共10行,就一共有9×10=90个差异值。最终生成哈希值即指纹。速度上来说,介于aHashpHash之间,精准度同样也介于aHashpHash之间。

结构相似性算法 SSIM

SSIM(structural similarity),结构相似性,是一种衡量两幅图像相似度的指标。SSIM算法主要用于检测两张相同尺寸的图像的相似度、或者检测图像的失真程度。原论文中,SSIM算法主要通过分别比较两个图像的亮度,对比度,结构,然后对这三个要素加权并用乘积表示。

SSIM算法在设计上考虑了人眼的视觉特性,它能够考虑到图像的结构信息在人的感知上的模糊变化,该模型还引入了一些与感知上的变化有关的感知现象,包含亮度mask和对比mask,结构信息指的是像素之间有着内部的依赖性,尤其是空间上靠近的像素点。这些依赖性携带着目标对象视觉感知上的重要信息。

经过调研对比,这里就选择SSIM算法。

2. 下载OpenCVSharp4

通过NuGet包管理器进行下载。搜索OpenCVSharp4下载。

请注意其描述信息:OpenCV wrapper for .NET. Since this package includes only core managed libraries, another package of native bindings for your OS is required (OpenCvSharp4.runtime.*).

这是说:OpenCV 包只是一个核心库,如需在你的系统上使用,还需要对应的运行时包,这里是Windows系统,因此还需下载 OpenCvSharp4.runtime.win

image


3. 使用

在项目中引入OpenCvSharp

using OpenCvSharp;

由于OpenCVSharp4没有直接提供封装SSIM算法的接口,因此需要自行写这部分代码。完整代码如下

public Scalar Compare_SSIM(string imgFile1, string imgFile2)
{
var image1 = Cv2.ImRead(imgFile1);
var image2Tmp = Cv2.ImRead(imgFile2);
// 将两个图片处理成同样大小,否则会有错误: The operation is neither ‘array op array’ (where arrays have the same size and the same number of channels), nor ‘array op scalar’, nor ‘scalar op array’
var image2 = new Mat();
Cv2.Resize(image2Tmp, image2, new OpenCvSharp.Size(image1.Size().Width, image1.Size().Height));
double C1 = 6.5025, C2 = 58.5225;
var validImage1 = new Mat();
var validImage2 = new Mat();
image1.ConvertTo(validImage1, MatType.CV_32F); //数据类型转换为 float,防止后续计算出现错误
image2.ConvertTo(validImage2, MatType.CV_32F);
Mat image1_1 = validImage1.Mul(validImage1); //图像乘积
Mat image2_2 = validImage2.Mul(validImage2);
Mat image1_2 = validImage1.Mul(validImage2);
Mat gausBlur1 = new Mat(), gausBlur2 = new Mat(), gausBlur12 = new Mat();
Cv2.GaussianBlur(validImage1, gausBlur1, new OpenCvSharp.Size(11, 11), 1.5); //高斯卷积核计算图像均值
Cv2.GaussianBlur(validImage2, gausBlur2, new OpenCvSharp.Size(11, 11), 1.5);
Cv2.GaussianBlur(image1_2, gausBlur12, new OpenCvSharp.Size(11, 11), 1.5);
Mat imageAvgProduct = gausBlur1.Mul(gausBlur2); //均值乘积
Mat u1Squre = gausBlur1.Mul(gausBlur1); //各自均值的平方
Mat u2Squre = gausBlur2.Mul(gausBlur2);
Mat imageConvariance = new Mat(), imageVariance1 = new Mat(), imageVariance2 = new Mat();
Mat squreAvg1 = new Mat(), squreAvg2 = new Mat();
Cv2.GaussianBlur(image1_1, squreAvg1, new OpenCvSharp.Size(11, 11), 1.5); //图像平方的均值
Cv2.GaussianBlur(image2_2, squreAvg2, new OpenCvSharp.Size(11, 11), 1.5);
imageConvariance = gausBlur12 – gausBlur1.Mul(gausBlur2);// 计算协方差
imageVariance1 = squreAvg1 – gausBlur1.Mul(gausBlur1); //计算方差
imageVariance2 = squreAvg2 – gausBlur2.Mul(gausBlur2);
var member = ((2 * gausBlur1.Mul(gausBlur2) + C1).Mul(2 * imageConvariance + C2));
var denominator = ((u1Squre + u2Squre + C1).Mul(imageVariance1 + imageVariance2 + C2));
Mat ssim = new Mat();
Cv2.Divide(member, denominator, ssim);
var sclar = Cv2.Mean(ssim);
return sclar; // 变化率,即差异
}

实际检测效果如下

image

这两幅图的相似度大约是92.21%,基本符合预期

image

这两幅图居然还有约18%的相似度,根据SSIM算法特性,这应该是图片大小的相似。

虽然也是拿来主义,毕竟我不是研究算法的大佬,需要站在巨人肩膀上干活~

做个笔记。

Visual Studio如何使用插件改变主题_visual studio 主题_Liquor999的博客-CSDN博客

mikel阅读(250)

Goodnight Theme

来源: Visual Studio如何使用插件改变主题_visual studio 主题_Liquor999的博客-CSDN博客

Visual Studio自带的主题太过于枯燥,此篇文章将详细讲述如何改变主题。

1.打开Visual Studio,在导航栏选择工具 -> 主题 -> 获取更多主题

此时浏览器会跳转到这个页面。选择一款自己喜欢的主题(可以搜索)点击,这里推荐One Dark Pro主题(本人VS里面有这个主题了,演示的时候使用Goodnight Theme)。

点开之后选择Download下载这个插件。

2.关闭Visual Studio,双击点开.vsix插件**

3.弹出来安装窗口,点击Install,即可成功安装。

安装成功,点击Close即可。

4.打开Visual Studio,在导航栏选择工具 -> 主题 -> Goodnight 就可以使用我们的下载的主题了。

5.效果对比
Goodnight主题效果如下图。

One Dark Pro主题效果如下图。

————————————————
版权声明:本文为CSDN博主「Liquor999」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_45959662/article/details/125696277

11个最佳Visual Studio主题合集排行:哪个最好?

mikel阅读(1394)

来源: 11个最佳Visual Studio主题合集排行:哪个最好?

开发者喜欢黑暗模式。当谈到生产力时,我们有意地将ide和代码编辑器的颜色方案从标准的纯白色改为相反的颜色。在某种程度上,我们这样做是为了把眼睛从耀眼的屏幕中拯救出来,减少潜在的压力。

最佳Visual Studio主题有哪些?但并非所有的黑暗主题都是平等的。它不仅仅是将配色方案从白色反转为黑色。随着时间的推移,函数、类、模块和代码部分的突出显示方式也可以决定你的整体生产力。正确的配色方案可以帮助你快速扫描并找出代码中的异常,本文为你介绍最佳Visual Studio主题合集。

什么是 Visual Studio 2019 主题?

主题是可切换的皮肤,可让你为Visual Studio 编辑器配置配色方案和字体。市场目前有 5000 多个可用主题,涵盖 Visual Studio 2019 和Visual Studio Code,每个主题都旨在以可视化方式优化代码的显示方式。这是通过预先配置函数、模块、类和代码部分的颜色来完成的,那么Visual Studio主题哪个最好

快速教程 – 如何安装 Visual Studio 主题

使用 Visual Studio 2019 而不是 Visual Studio Code 的好处是,除了拥有与 Azure 服务直接集成的工具之外,还可以跨设备同步设置。这意味着即使你不在常用的工作机器上,你的主题配置也能随心所欲。

安装 Visual Studio 主题的最简单方法是导航到菜单栏中的“扩展”选项,然后选择“管理扩展”。

11个最佳Visual Studio主题合集排行:哪个最好?

下一步是在左侧面板的 Online 下选择 Visual Studio Marketplace。

11个最佳Visual Studio主题合集排行:哪个最好?

完成后,搜索你想要的主题并在搜索结果中选择它。

11个最佳Visual Studio主题合集排行:哪个最好?

下载主题后,关闭 VS2019,它将开始安装主题并创建一个如下所示的提示:

11个最佳Visual Studio主题合集排行:哪个最好?
11个最佳Visual Studio主题合集排行:哪个最好?
11个最佳Visual Studio主题合集排行:哪个最好?
11个最佳Visual Studio主题合集排行:哪个最好?

完成此操作后,再次打开 Visual Studio 2019 并导航到位于“工具”选项卡下的“选项”面板。选择颜色主题选项并选择你要应用的主题。

11个最佳Visual Studio主题合集排行:哪个最好?
11个最佳Visual Studio主题合集排行:哪个最好?

这基本上就是在 Visual Studio 中安装和切换主题的过程。事不宜迟,让我们回顾一下顶级 VS Code 主题。

由于Visual Studio Code 是市场上最受欢迎的代码编辑器之一,Visual Studio 2019 获得了共享市场和社区规模的好处。说到主题,有很多可用的主题。但是你应该选择哪一个呢?这是其他开发人员使用的前 11 个 Visual Studio 主题的列表。

前 11 个 最佳Visual Studio主题合集

1.One Dark Pro

最佳Visual Studio主题有哪些?除了标准的 Visual Studio 暗模式,One Dark Pro也是 Visual Studio 安装最多的主题之一。

它由 binaryify 创建,已下载超过 91,000 次,并将 Atom 的原始标志性 One Dark 主题移植到 Visual Studio 2019 中。

One Dark Pro 的一个特殊功能是突出显示语法支持 Markdown 预览——这在编写文档时可以派上用场。Markdown 并不适合所有人,能够区分不同的样式可以让生活更轻松。

11个最佳Visual Studio主题合集排行:哪个最好?

2. Midnight Spruce Pine

Visual Studio主题哪个最好Midnight Spruce Pine是一个黑暗主题,融合了 Tim Macharia 的 Pine Gap Dark 主题和 VS Code Ayu Mirage 的最佳部分。它在方法的黄色突出显示和变量、函数和类的蓝色主题上运行。这使得基于层次结构及其作用域位置跟踪和扫描代码变得容易。

11个最佳Visual Studio主题合集排行:哪个最好?

3. Atom One Dark Theme

Atom One Dark Theme是另一个 VS Code 主题,它来自Atom的原始 One Dark 主题。它是 One Dark Pro 的替代品,并且基于 250 万次下载证明很受欢迎。

11个最佳Visual Studio主题合集排行:哪个最好?

4.Goodnight Theme

Goodnight Theme是 Visual Studio 的深色主题,针对 C#、JSON、XML 和 Razor 进行了优化。方法以黄色突出显示,而数组以对比鲜明的水绿色突出显示。晚安主题中最重要的值是变量,其中黑色背景之间的对比度通过白色突出显示达到最大。

11个最佳Visual Studio主题合集排行:哪个最好?

5.Midnight Deep

最佳Visual Studio主题有哪些?Midnight Deep是 VS2019 的主题,适用于喜欢具有最大对比度效果的深色主题的开发人员。纯黑色背景确保了这是可能的,并为弱光情况打造了一个梦幻般的主题。如果你是那种喜欢熬夜工作或喜欢在大量人造光下工作的开发人员,Midnight Deep 可能就是适合你的 Visual Studio 主题。

11个最佳Visual Studio主题合集排行:哪个最好?

6. Night Owl

Visual Studio主题哪个最好?Night Owl是一个漂亮的小黑暗主题,不仅仅是反转颜色。Night Owl 的创造者确保将色盲人士和在弱光环境中工作的人士纳入配色方案的考虑中。

11个最佳Visual Studio主题合集排行:哪个最好?

7. Monokai Night

最佳Visual Studio主题合集:Monokai Night是一种因其功能性和美感而被选择的调色板。它最初由 Wimer Hazenberg 于 2006 年创建,并被采纳为 Sublime Text 的原始主题。

如果你是 Sublime Text 用户,那么Monokai Night 会让你熟悉 Visual Studio。

11个最佳Visual Studio主题合集排行:哪个最好?

8.Voyager Theme

Voyager Theme于 2020 年发布,受到 Jetbrains 的启发,包含在带有橙色、绿色、浅黄色和白色调色板的深色背景下突出显示的文本。与非黑色背景的对比度根据代码中类型的重要性而增加。例如,评论是可读的,但除非我们去搜索它们,否则不会跳出来。但是,类具有最大对比度,以便于扫描和跟踪。

11个最佳Visual Studio主题合集排行:哪个最好?

9. Pine Gap Dark

Pine Gap Dark是 VS2019 的主题,其灵感来自Ayu Mirage 的调色板,带有芥末黄色突出显示,使你的标签流行起来。这个主题非常适合 JavaScript、CSS/SCSS、C# 和 JSON,并且是使用 Microsoft 的 Visual Studio颜色主题设计器创建的

11个最佳Visual Studio主题合集排行:哪个最好?

10. MonokaiVS

Visual Studio主题哪个最好?MonokaiVS主题将两个流行的主题融合在一起,形成一个史诗主题。One Monokai 从 Monokai 的 One Dark 主题和颜色原则中汲取了最佳部分,以可视化地优化 Visual Studio 上的代码输出。

11个最佳Visual Studio主题合集排行:哪个最好?

11. Midnight Lights

最佳Visual Studio主题有哪些?如果黑色不是你喜欢的配色方案,Midnight Lights 会用一些不同的东西来营造黑暗模式的氛围。深蓝色背景散发出夜间氛围。它还通过为类和函数提供各种深浅不一的蓝色来营造氛围,同时以最大对比度突出显示新方法和变量。

11个最佳Visual Studio主题合集排行:哪个最好?

最佳Visual Studio主题合集总结

在编码方面,暗模式不仅仅是暗模式。除了使其具有视觉吸引力之外,它还使我们的代码在视觉上更易于跟踪和跟踪。

大多数深色主题都遵循相同的格式——最重要信息的最高对比度。根据主题创建者的偏好,它可能意味着作为突出显示元素的函数和类与变量和数组之间的差异。

Vue在ASP.NET MVC中的进行前后端的交互 - Kaden - 博客园

mikel阅读(303)

来源: Vue在ASP.NET MVC中的进行前后端的交互 – Kaden – 博客园

Preface:

由于最近在研究前端相关的技术,作为前端非常优秀的框架Vue,个人在学习的过程中遇到一些问题,网上相关资料有限,所以在这这里总结一下个人使用Vue的一点经验,以便后来者借鉴!

官方文档:Vue.js

使用Vue在ASP.NET MVC中进行前后端交互
在阅读下面的文章之前你需要先了解一下Vue官方推荐的前后端交互的插件:

1.resource(官方在2.0版本之后取消了对此插件的维护)

2.axios

注:这里使用的都是异步的插件,因为这样才会在你的项目中具有使用意义,当然你也可以用其它的js库,如JQuery、Fetch等等…

Instance:

Controller

复制代码
 1 using Demo.Models;
 2 using System;
 3 using System.Collections.Generic;
 4 using System.Linq;
 5 using System.Web;
 6 using System.Web.Mvc;
 7 
 8 namespace Demo.Controllers
 9 {
10     //[RoutePrefix("api/Goods")]
11     public class GoodsController : Controller
12     {
13         List<GoodsEntity> goosdList = new List<GoodsEntity>
14         {
15             new GoodsEntity(){ ID=001,Name="水",Type=1,Price=3},
16             new GoodsEntity(){ ID=002,Name="牛奶",Type=1,Price=10},
17             new GoodsEntity(){ ID=003,Name="面包",Type=2,Price=15}
18         };
19 
20         // GET: Goods
21         public ActionResult Index()
22         {
23             return View();
24         }
25 
26         public ActionResult Check()
27         {
28             return View();
29         }
30 
31         [HttpGet]
32         public JsonResult GetGoodsType()
33         {
34             List<int> goodsType = new List<int>();
35             foreach (var item in goosdList)
36             {
37                 if (!goodsType.Contains(item.Type))
38                 {
39                     goodsType.Add(item.Type);
40                 }
41             }
42             return Json(goodsType, JsonRequestBehavior.AllowGet);
43         }
44 
45         [HttpGet]
46         public JsonResult GetAllGoods()
47         {
48             return Json(goosdList, JsonRequestBehavior.AllowGet);
49         }
50 
51         [HttpPost]
52         public JsonResult GetGoods(int id)
53         {
54             var entity = goosdList.Where(g => g.ID == id).FirstOrDefault();
55             if (entity != null)
56             {
57                 return Json(new ReturnJsonInfo(500, "success!", entity));
58             }
59             return Json(new ReturnJsonInfo(400, "error!", null));
60         }
61 
62         [HttpPost]
63         public JsonResult UpdateGoods(GoodsEntity entity)
64         {
65             if (entity!=null)
66             {
67                 var goodsEntiy = goosdList.FirstOrDefault(g => g.ID == entity.ID);
68                 if (goodsEntiy!=null)
69                 {
70                     goodsEntiy = entity;
71                     return Json(new ReturnJsonInfo(500, "success!", goosdList));
72                 }
73                 goosdList.Add(entity);
74                 return Json(new ReturnJsonInfo(500, "success!", goosdList));
75             }
76             return Json(new ReturnJsonInfo(400, "error!",null));
77         }
78 
79         [HttpPost]
80         public JsonResult DelectGoods(int id)
81         {
82             var entity = goosdList.Where(g => g.ID == id).FirstOrDefault();
83             if (entity != null)
84             {
85                 goosdList.Remove(entity);
86                 return Json(new ReturnJsonInfo(500, "success!", goosdList));
87             }
88             return Json(new ReturnJsonInfo(400, "error!",null));
89         }
90 
91     }
92 }
复制代码

在上面的控制器中加载了一些示例数据,并且都是以json的格式返回前端,这样前端就可以直接使用这些数据。

注:控制器返回至前端的json中,上面使用 “ReturnJsonInfo” 对象序列化进行返回, “ReturnJsonInfo” 代码如下。

复制代码
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Web;
 5 
 6 namespace Demo.Models
 7 {
 8     public class ReturnJsonInfo
 9     {
10         public int Code { get; set; }
11         public string Message { get; set; }
12         public object Entity { get; set; }
13         public ReturnJsonInfo(int code, string message,object obj)
14         {
15             this.Code = code;
16             this.Message = message;
17             this.Entity = obj;
18         }
19     }
20 }
复制代码

 

View

1.前端采用resource插件

 

复制代码
  1 @{
  2     ViewBag.Title = "Goods IndexPage";
  3 }
  4                  <script type="text/javascript" src="~/Resources/Scripts/vue.js"></script>
  5                  <script type="text/javascript" src="~/Resources/Scripts/vue-resource.js"></script>
  6 <h2>Index</h2>
  7                <div id="demo">
  8                       <table>
  9                           <tr>
 10                               <td><label>编号:</label></td>
 11                               <td><input type="text" v-model="newGoods.id" /></td>
 12 
 13                               <td><label>名称:</label></td>
 14                               <td><input type="text" v-model="newGoods.name" /></td>
 15 
 16                               <td><label>类型:</label></td>
 17                               <td><input type="text" v-model="newGoods.type" /></td>
 18 
 19                               <td><label>售价:</label></td>
 20                               <td><input type="text" v-model="newGoods.price" /></td>
 21 
 22                               <td><input type="submit" value="查询" v-on:click="GetGoods(newGoods.id)" /></td>
 23                           </tr>
 24                       </table>
 25                               <table v-show="goodsList.length">
 26                                   <tr>
 27                                       <td>编号</td>
 28                                       <td>名称</td>
 29                                       <td>类型</td>
 30                                       <td>售价</td>
 31                                   </tr>
 32                                   <tr v-for="item in goodsList">
 33                                       <td>{{item.ID}}</td>
 34                                       <td>{{item.Name}}</td>
 35                                       <td>{{item.Type}}</td>
 36                                       <td>{{item.Price}}</td>
 37                                   </tr>
 38                               </table>
 39                </div> 
 40 <script type="text/javascript">
 41     var view = new Vue(
 42         {
 43             el: "#demo",
 44             data: {
 45                 goodsList: [],
 46                 newGoods: {id:'',name:'',type:'',price:''}
 47             },              
 48             created: function () {
 49                 this.InIt();
 50             },
 51             methods: {
 52                 InIt: function () {
 53                     //初始化
 54                     this.GetAllGoods();
 55                 },
 56                 GetAllGoods: function () {
 57                     var _self = this;               
 58                     _self.$http.get("../Goods/GetAllGoods").then(
 59                         // Lambda写法
 60                         (response) => {
 61                             //successCallback
 62                             for (var i = 0; i < response.data.length; i++) {
 63                                 _self.goodsList.push(response.data[i]);                                   
 64                             }                                     
 65                         } ,
 66                         (response) => {
 67                                    //errorCallback   
 68                         }
 69                     );
 70                 },
 71                 GetGoods: function (_id) {
 72                     var _self = this;
 73                     _self.goodsList = [];
 74                     if (_id.length > 0) {
 75                         _self.$http.post("../Goods/GetGoods", { id: _id }).then(
 76                             // 传统写法
 77                             function (response) {
 78                                 //successCallback                                    
 79                                 if (response.data.Code == 500) {
 80                                     _self.goodsList.push(response.data.Entity);
 81                                 }
 82                                 else {
 83                                     alert(response.data.Message);
 84                                 }
 85                             },
 86                             function (response) {
 87                                 //errorCallback
 88                             }
 89                         )
 90                             .catch(function (response) {
 91                                 console.log(response);
 92                             });
 93                     }
 94                     else {
 95                         _self.GetAllGoods();
 96                     }
 97             }
 98             }
 99         }
100     );              
101 </script>
复制代码

 

2.前端采用axios插件

复制代码
  1 @{
  2     Layout = null;
  3 }
  4 
  5 <!DOCTYPE html>
  6 
  7 <html>
  8 <head>
  9     <meta name="viewport" content="width=device-width" />
 10     <title>Check</title>
 11     <script type="text/javascript" src="~/Resources/Scripts/vue.js"></script>
 12     <script type="text/javascript" src="~/Resources/Scripts/axios.min.js"></script>
 13 </head>
 14 <body>
 15     <div id="demo">
 16         <div>
 17             <table>
 18                 <tr>
 19                     <td><label>编号:</label></td>
 20                     <td><input type="text" v-model="newGoods.id" /></td>
 21 
 22                     <td><label>名称:</label></td>
 23                     <td><input type="text" v-model="newGoods.name" /></td>
 24 
 25                     <td><label>类型:</label></td>
 26                     <td>
 27                         <select v-model="newGoods.type">
 28                             <option value="">---ALL---</option>
 29                             <option v-for="type in goodsType" v-bind:value="type">{{type}}</option>
 30                         </select>
 31                     </td>
 32 
 33                     <td><label>售价:</label></td>
 34                     <td><input type="text" v-model="newGoods.price" /></td>
 35 
 36                     <td>
 37                         <input type="submit" value="查询" v-on:click="GetGoods(newGoods.id)" />
 38                         <input type="submit" value="更新" v-on:click="UpdateGoods(newGoods.id,newGoods.name,newGoods.type,newGoods.price)" />
 39                         <input type="submit" value="删除" v-on:click="DelectGoods(newGoods.id)" />
 40                     </td>
 41                 </tr>
 42             </table>
 43         </div>
 44         <div>
 45             <table v-show="goodsList.length">
 46                 <tr>
 47                     <td>行号</td>
 48                     <td>编号</td>
 49                     <td>名称</td>
 50                     <td>类型</td>
 51                     <td>售价</td>
 52                 </tr>
 53                 <tr v-for="(item,index) in goodsList">
 54                     <td>{{index+1}}</td>
 55                     <td>{{item.ID}}</td>
 56                     <td>{{item.Name}}</td>
 57                     <td>{{item.Type}}</td>
 58                     <td>{{item.Price}}</td>
 59                 </tr>
 60             </table>
 61         </div>
 62     </div>
 63 
 64     
 65     <script type="text/javascript">
 66         var vm = new Vue({
 67             el: "#demo",
 68             data: {
 69                 goodsType:[],
 70                 goodsList: [],
 71                 newGoods: { id: '', name: '', type: '', price: '' }
 72             },
 73             mounted() {
 74                 this.GetGoodsType();
 75                 this.GetAllGoods();
 76             },
 77             methods:
 78             {
 79                 GetGoodsType: function () {
 80                     axios.get("../Goods/GetGoodsType").then(
 81                         (response) => {
 82                             this.goodsType = [];
 83                             for (var i = 0; i < response.data.length; i++) {
 84                                 this.goodsType.push(response.data[i]);
 85                             }
 86                         },
 87                         (response) => {
 88                             alert(response.status);
 89                         }
 90                     )
 91                         .catch(function (response) {
 92                             console.log(response);
 93                         });
 94                 } ,
 95                 GetAllGoods: function () {
 96                     axios.get('../Goods/GetAllGoods').then(
 97                         function (response) {                               
 98                             vm.goodsList = [];  
 99                             for (var i = 0; i < response.data.length; i++) {      
100                                 vm.goodsList.push(response.data[i]);
101                             }
102                             //vm.goodsList.splice(response.data.length);
103                         },
104                         function (response) {
105                             alert(response.status);
106                         }
107                     )
108                         .catch(
109                         function (error) {
110                             alert(error);
111                         }
112                         )
113                 },
114                 GetGoods: function (_id) {
115                     if (_id.length > 0) {
116                         axios.post("../Goods/GetGoods", { id: _id }).then(                                   
117                             (response) => {        
118                                 this.goodsList = [];
119                                 if (response.data.Code == 500) {
120                                     this.goodsList.push(response.data.Entity);   
121                                 }
122                                 else {
123                                     alert(response.data.Message);
124                                 }
125                                  
126                             },
127                             (response) => {       
128                                 alert(response.status);
129                             }
130                         )
131                             .catch(function (response) {
132                                 console.log(response);
133                             });
134                     }
135                     else {
136                         this.GetAllGoods();
137                     }
138                 },
139                 UpdateGoods: function (_id,_name,_type,_price) {
140                     axios.post("../Goods/UpdateGoods", { entity: { ID: _id, Name: _name, Type: _type, Price: _price } }).then(                              
141                         (response) => {
142                             this.goodsList = [];
143                             if (response.data.Code == 500) { 
144                                 for (var i = 0; i < response.data.Entity.length; i++) {
145                                     this.goodsList.push(response.data.Entity[i]);
146                                 }
147                             }
148                             else {
149                                 alert(response.data.Message);
150                             }
151                         },
152                         (response) => {
153                             alert(response.status);
154                         }
155                     )
156                         .catch(function (response) {
157                             console.log(response);
158                         });
159                 },
160                 DelectGoods: function (_id) {
161                     axios.post("../Goods/DelectGoods", { id: _id }).then(
162                         (response) => {
163                             this.goodsList = [];
164                             if (response.data.Code == 500) {
165                                 for (var i = 0; i < response.data.Entity.length; i++) {
166                                     this.goodsList.push(response.data.Entity[i]);
167                                 }
168                             }
169                             else {
170                                 alert(response.data.Message);
171                             }
172 
173                         },
174                         (response) => {
175                             alert(response.status);
176                         }
177                     )
178                         .catch(function (response) {
179                             console.log(response);
180                         });
181                 } 
182             },
183 
184         });
185     </script>
186 </body>
187 </html>
复制代码

在上面的视图中,前端都是用数组进行填充的,值得注意的是vue在数组监听这一块做得并不是特别友好。但也提供了一些非常友好的api。

如果你也采用上述方式更新view,请参阅vue官方提供的关于操作数组方法

 

Perorations:

在上面的Demo中,采用的是ASP.NET MVC模板。在写View部分的时候感觉确实比较方便,不需要再去写获取元素的代码,只需要把数据绑定至元素上,关注数据的变动就可以了。

当然你也可以选择Razor语法,只不过那样看起来并不是很友善。

 

以上是个人在写这个Demo之后的一些总结。如有描述不当,请@作者修改,谢谢!