首页 > 数据库 >asp.netcore8 + vue3 + mysql 自用记账项目(三)功能开发

asp.netcore8 + vue3 + mysql 自用记账项目(三)功能开发

时间:2024-09-11 12:03:31浏览次数:9  
标签:asp const name value netcore8 vue vue3 import options

一、前端

  前端使用 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

相关文章

  • asp.netcore8 + vue3 + mysql 自用记账项目(一)背景简介
    一、背景18年的时候,用了一年多第三方免费的记账本不用了,两个方面原因,一是随着数据增多,APP用着越来越慢,二是相关数据被用于其他用途的风险很大且广告很烦。所以,后面通过MUI+asp.netcore+sqlserver实现记账web功能,在阿里云1核2G服务器的windows系统上发布了自用的服务,最......
  • vue3 中使用 icon 图标的 3 种方案
    1.element-iconElementPlus提供了一套常用的图标集合。1.1.安装#选择一个你喜欢的包管理器#NPM$npminstall@element-plus/icons-vue#Yarn$yarnadd@element-plus/icons-vue#pnpm$pnpminstall@element-plus/icons-vue#选择一个你喜欢的包管理器1.2.......
  • 使用Vue3.5的onWatcherCleanup封装自动cancel的fetch函数
    前言在欧阳的上一篇这应该是全网最详细的Vue3.5版本解读文章中有不少同学对Vue3.5新增的onWatcherCleanup有点疑惑,这个新增的API好像和watchAPI回调的第三个参数onCleanup功能好像重复了。今天这篇文章来讲讲新增的onWatcherCleanup函数的使用场景:封装一个自动cancel的fetch函......
  • 使用VSCode搭建UniApp + TS + Vue3 + Vite项目
    uniapp是一个使用Vue.js开发所有前端应用的框架,开发者编写一套代码,可发布到iOS、Android、以及各种小程序。深受广大前端开发者的喜爱。uniapp官方也提供了自己的IDE工具HBuilderX,可以快速开发uniapp项目。但是很多前端的同学已经比较习惯使用VSCode去开发项目,为了开发uniapp项目......
  • 学习Vue3的第三天
    Vue3生命周期概念:生命周期钩子是Vue组件在其生命周期内不同阶段触发的函数,允许开发者在这些关键时刻插入自定义逻辑。规律:生命周期整体分为四个阶段,分别是:创建、挂载、更新、销毁,每个阶段都有两个钩子,一前一后。Vue2生命周期钩子创建阶段beforeCreate:组件实例刚创......
  • vue3 内置特殊元素<slot> 与 插槽 Slots
    vue官网内置特殊元素<slot>插槽Slots<slot><slot>元素是一个插槽出口(slotoutlet),标示了父元素提供的插槽内容(slotcontent)将在哪里被渲染。Vue模板里的<slot>元素会被编译到JavaScript,因此不要与原生<slot>元素进行混淆。<slot>元素可以使用nameat......
  • vue3快速上手和基本特性
    1)vue使用方式1)cdn方式    该方式无需使用包管理工具,只要用script标签引入js文件即可,可以快速使用vue<scriptsrc="https://unpkg.com/vue@3/dist/vue.global.js"></script>2)创建vue项目    创建vue项目时需要nodejs环境,安装好nodejs后先为nodejs换一个国......
  • 【卷起来】VUE3.0教程-05-侦听器
    =========各位看官,在开始学习之前,请帮我点个关注和赞吧========== ......
  • 工作日志:从零搭建vue3+ts+sass项目(3)
    折腾了两天后,发现elementPlus的代码都是ts的,本来不想现在用ts,因为非常不熟悉,但长痛不如短痛,重开项目!直接vue3+vite+ts+sass!让暴风雨来得更猛烈一下吧!我差那几个bug吗?1、执行命令如下:npmcreatevite@latestnpminstallnpminstallsass-Dnpmivue-router2、在src下......
  • Vue3项目开发——新闻发布管理系统(六)
    文章目录八、首页设计开发1、页面设计2、登录访问拦截实现3、用户基本信息显示①封装用户基本信息获取接口②用户基本信息存储③用户基本信息调用④用户基本信息动态渲染4、退出功能实现①注册点击事件②添加退出功能③数据清理5、代码下载......