一、前端
前端使用 vue3 + vant4 组件实现页面功能。
1、创建vue3项目
各个操作分别是:
- 选择创建模式?手动创建
- 选择项目模块?Babel,Kouter, Wuex,CSS Pre processors
- 选择vue版本?3.0
- 是否使用历史路由模式?是
- 样式的写法?Less
- 项目配置放在哪?package.json文件里
- 是否保存本次选择?否
2、启动项目
通过cd "D:\VS2019\Git\我的项目\my_vue_test"进入文件夹后,执行npm run serve可以启动项目,执行npm run build可以打包项目。
3、axios请求配置
import axios from "axios";
import router from "@/router/index";
axios.defaults.baseURL = "http://ip:端口";
axios.interceptors.request.use((config) => {
// 为请求头添加token
config.headers["Authorization"] = "Bearer " + sessionStorage.getItem("token");
config.headers["UserId"] = sessionStorage.getItem("UserId");
// return config是固定用法 必须有返回值
return config;
});
// 添加响应拦截器
axios.interceptors.response.use(
function (response) {
// 对响应数据做点什么
if (response.status === 200) {
return response.data;
} else {
return response;
}
},
function (error) {
// 判断token是否失效
if (
error.response &&
(error.response.status === 401 || error.response.status === 504)
) {
router.push({ name: "Login" });
}
// 对响应错误做点什么
return Promise.reject(error);
}
);
export default axios;
4、路由配置
import { createRouter, createWebHashHistory } from "vue-router";
import LoginView from "../views/Login.vue";
import IndexView from "../views/Index.vue";
import ReportFormsView from "../views/ReportForms.vue";
import AddOrUpdateView from "../views/AddOrUpdate.vue";
import SearchDataView from "../views/SearchData.vue";
import AboutView from "../views/About.vue";
const routes = [
{
path: "/",
name: "Login",
component: LoginView,
},
{
path: "/Index",
name: "Index",
component: IndexView,
},
{
path: "/ReportForms",
name: "ReportForms",
component: ReportFormsView,
},
{
path: "/AddOrUpdate/:ObjectId",
name: "Update",
component: AddOrUpdateView,
},
{
path: "/AddOrUpdate",
name: "Add",
component: AddOrUpdateView,
},
{
path: "/SearchData",
name: "SearchData",
component: SearchDataView,
},
{
path: "/About",
name: "About",
component: AboutView,
},
];
const router = createRouter({
history: createWebHashHistory(),
routes,
});
export default router;
5、全局配置
import { createApp } from "vue";
import App from "./App.vue";
import "./registerServiceWorker";
import router from "./router";
import store from "./store";
// 定义特性标志
window.__VUE_OPTIONS_API__ = true;
window.__VUE_PROD_DEVTOOLS__ = false;
window.__VUE_PROD_HYDRATION_MISMATCH_DETAILS__ = false;
const app = createApp(App).use(store).use(router);
// 定义全局方法
app.config.globalProperties.$SystemFunc = {
ValidityLoginState() {
let token = sessionStorage.getItem("token");
if (token == null || token == "" || token == undefined) {
router.push({ name: "Login" });
}
},
};
app.mount("#app");
6、代理配置
const { defineConfig } = require("@vue/cli-service");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = defineConfig({
transpileDependencies: true,
configureWebpack: {
plugins: [
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css",
}),
],
},
devServer: {
open: true,
host: "localhost",
port: 8080,
https: false,
proxy: {
"/api": {
target: "http://localhost:8001",
// target: "http://localhost:5124",
ws: true,
changeOrigin: true,
},
},
},
});
7、统计图表实现
<template>
<div class="ReportForms">
<MyNavBar
:title="'统计'"
:leftArrow="true"
:rightText="'查询'"
:onParentClickLeft="onClickLeft"
:onParentClickRight="getLineData"
/>
<canvas ref="chartContainer"></canvas>
<van-form>
<van-cell-group inset>
<van-field name="radio" label="年月">
<template #input>
<van-radio-group v-model="yearOrMonth" direction="horizontal">
<van-radio name="1">年</van-radio>
<van-radio name="2">月</van-radio>
</van-radio-group>
</template>
</van-field>
<van-field
v-model="startDateStr"
is-link
readonly
name="datePicker"
label="开始时间"
@click="showStart = true"
/>
<van-popup v-model:show="showStart" position="bottom">
<van-date-picker
v-model="startDate"
:formatter="dateFormatter"
:columns-type="columnsType"
@confirm="startDateConfirm"
@cancel="showStart = false"
/>
</van-popup>
<van-field
v-model="endDateStr"
is-link
readonly
name="datePicker"
label="结束时间"
@click="showEnd = true"
/>
<van-popup v-model:show="showEnd" position="bottom">
<van-date-picker
v-model="endDate"
:formatter="dateFormatter"
:columns-type="columnsType"
@confirm="endDateConfirm"
@cancel="showEnd = false"
/>
</van-popup>
</van-cell-group>
</van-form>
<MyTabs />
</div>
</template>
<script>
import { onMounted, ref } from "vue";
import axhttp from "@/utils/http";
import { ValidityLoginState } from "@/utils/validityLogin";
import {
Form,
Field,
CellGroup,
RadioGroup,
Radio,
DatePicker,
Popup,
} from "vant";
import { Chart, registerables } from "chart.js";
import MyTabs from "@/components/MyTabs.vue";
import MyNavBar from "@/components/MyNavBar.vue";
Chart.register(...registerables);
export default {
name: "ReportFormsView",
components: {
[Form.name]: Form,
[Field.name]: Field,
[CellGroup.name]: CellGroup,
[RadioGroup.name]: RadioGroup,
[Radio.name]: Radio,
[DatePicker.name]: DatePicker,
[Popup.name]: Popup,
MyTabs,
MyNavBar,
},
setup() {
let date = new Date();
let y = date.getFullYear();
let m = date.getMonth() + 1;
const yearOrMonth = ref("1");
const startDateStr = ref(`${y - 1}-${m > 9 ? m : "0" + m}`);
const endDateStr = ref(`${y}-${m > 9 ? m : "0" + m}`);
const startDate = ref([y + "", m > 9 ? m : "0" + m]);
const endDate = ref([y + "", m > 9 ? m : "0" + m]);
const showStart = ref(false);
const showEnd = ref(false);
const columnsType = ["year", "month"];
const onClickLeft = () => history.back();
const startDateConfirm = (data) => {
let y = parseInt(data.selectedValues[0]);
let m = parseInt(data.selectedValues[1]);
startDate.value = [y + "", m > 9 ? m : "0" + m];
startDateStr.value = `${y}-${m > 9 ? m : "0" + m}`;
showStart.value = false;
};
const endDateConfirm = (data) => {
let y = parseInt(data.selectedValues[0]);
let m = parseInt(data.selectedValues[1]);
endDate.value = [y + "", m > 9 ? m : "0" + m];
endDateStr.value = `${y}-${m > 9 ? m : "0" + m}`;
showEnd.value = false;
};
const dateFormatter = (type, option) => {
if (type === "year") {
option.text += "年";
}
if (type === "month") {
option.text += "月";
}
return option;
};
const getLineData = () => {
axhttp
.post("/api/Content/GetLineData", {
YearOrMonth: yearOrMonth.value,
StartDate: startDateStr.value,
EndDate: endDateStr.value,
})
.then((res) => {
// 处理返回的认证结果
if (res.Success) {
showLineChart(res.Data);
}
})
.catch((error) => {
// 处理错误情况
alert(error);
});
};
const chartContainer = ref(null);
const chartDataModal = ref(null);
const showLineChart = (dt) => {
if (chartContainer.value) {
const ctx = chartContainer.value.getContext("2d");
if (chartDataModal.value != null) {
chartDataModal.value.destroy();
}
chartDataModal.value = new Chart(ctx, {
type: "line", // 折线图
data: dt,
});
}
};
onMounted(() => {
ValidityLoginState();
getLineData();
});
return {
yearOrMonth,
startDateStr,
endDateStr,
startDate,
endDate,
showStart,
showEnd,
columnsType,
chartContainer,
chartDataModal,
onClickLeft,
dateFormatter,
startDateConfirm,
endDateConfirm,
showLineChart,
getLineData,
};
},
};
</script>
配置完成后,就可以一边测试,一边实现功能。
二、后端
1、Program.cs配置
日志:
var appName = "test_api";
var seqServerUrl = builder.Configuration["SeqServerUrl"];
var logger = new LoggerConfiguration()
.ReadFrom.Configuration(builder.Configuration)
.Enrich.WithProperty("ApplicationName", appName)
.Enrich.WithSpan()
.WriteTo.Console()
.WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl)
.CreateLogger();
builder.Host.UseSerilog(logger, true);
IdentityServer4:
var certPem = builder.Configuration.GetValue<string>("HttpsCert:PemPath");
var keyPem = builder.Configuration.GetValue<string>("HttpsCert:PrivateKeyPath");
var cert = X509Certificate2.CreateFromPemFile(certPem, keyPem);
builder.WebHost.ConfigureKestrel(serverOptions =>
{
serverOptions.ConfigureHttpsDefaults(listenOptions =>
{
listenOptions.ClientCertificateMode = ClientCertificateMode.AllowCertificate;
listenOptions.ServerCertificate = cert;
listenOptions.SslProtocols = SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Tls13;
});
});
var authority = builder.Configuration.GetValue<string>("ClientCredentialsUserOptions:Authority");
builder.Services.AddIdentityServer(option => option.IssuerUri = authority)
.AddInMemoryIdentityResources(Config.IdentityResources)
.AddInMemoryApiScopes(Config.ApiScopes)
.AddInMemoryApiResources(Config.ApiResources)
//.AddInMemoryClients(Config.Clients)
.AddInMemoryClients(builder.Configuration.GetSection("ClientCredentialsUserOptions:Clients"))
.AddResourceOwnerValidator<CustomerResourceOwnerPasswordValidator>()
//.AddDeveloperSigningCredential();
.AddSigningCredential(cert);
builder.Services.AddSingleton<ICorsPolicyService>(new DefaultCorsPolicyService(new LoggerFactory().CreateLogger<DefaultCorsPolicyService>()) { AllowAll = true });
builder.Services.AddAuthorization();
builder.Services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(options =>
{
options.Authority = authority;
options.RequireHttpsMetadata = false;
options.ApiName = "api1";
options.ApiSecret = ""; //对应ApiResources中的密钥
});
Swagger:
builder.Services.AddSwaggerGen(options =>
{
//options.DescribeAllEnumsAsStrings();
options.SwaggerDoc("v1", new OpenApiInfo
{
Title = "PKPM CenterService API",
Version = "v1",
Description = "PKPM CenterService Microservice HTTP API."
});
//// 为 Swagger JSON and UI设置xml文档注释路径
//var basePath = Path.GetDirectoryName(typeof(Program).Assembly.Location);//获取应用程序所在目录(绝对,不受工作目录影响,建议采用此方法获取路径)
//var xmlPath = Path.Combine(basePath, "PKPM.CloundAccount.CenterService.xml");
//options.IncludeXmlComments(xmlPath);
// To Enable authorization using Swagger (JWT)
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme()
{
Name = "Authorization",
Type = SecuritySchemeType.ApiKey,
Scheme = "Bearer",
BearerFormat = "JWT",
In = ParameterLocation.Header,
Description = "JWT Authorization header using the Bearer scheme. \r\n\r\n Enter 'Bearer' [space] and then your token in the text input below.\r\n\r\nExample: \"Bearer 12345abcdef\"",
});
options.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
new string[] {}
}
});
});
跨域策略:
// 添加跨域策略
builder.Services.AddCors(options =>
{
// 配置默认策略和中间件:options.AddDefaultPolicy(policy =>{policy.WithOrigins("");});app.UseCors(); // 将自动应用于所有控制器终结点
//options.AddPolicy("CorsPolicyName", policy =>
options.AddDefaultPolicy(policy =>
{
policy
// AllowAnyOrigin 允许任何源地址的访问
//.AllowAnyOrigin()
// 仅允许一个地址访问
.WithOrigins("http://IP:Port")
// 允许多个地址访问,英文逗号分隔
//.WithOrigins("http://IP:Port","http://IP1:Port1","http://IP2:Port2")
// 支持同时允许多个指定地址的访问
//.WithOrigins(new string[]{"http://IP1:Port1","http://IP2:Port2","http://IP3:Port3"})
// 允许任何的Header头部标题
.AllowAnyHeader()
// 自定义请求头
//.WithHeaders("Account", "ClientType", "OrgId", "Token", "Department", "EntAuthVebr")
// 允许任何方法
.AllowAnyMethod()
// 允许的谓词方法
//.WithMethods(HttpMethods.Options, HttpMethods.Get, HttpMethods.Post, HttpMethods.Put, HttpMethods.Delete)
// 允许跨源请求发送凭据 允许时 Origin 不允许为“*”
.AllowCredentials()
// 设置预检请求的最大缓存时间
.SetPreflightMaxAge(TimeSpan.FromHours(24));
});
});
完成上述配置后,就可以实现接口,与前端对接之后实现功能。
标签:asp,const,name,value,netcore8,vue,vue3,import,options From: https://www.cnblogs.com/lx-bk/p/18398881