vue3+element-plus+flask 简易【工作日志本】小软件(过程超详细)

ops/2024/10/20 11:28:00/

终于有时间继续学习技术了!开发了一个简易的用于记录日常工作内容的小软件,权当学习和练手。功能如下:用户登录、日志内容的查、增、删、改以及导出。
开发环境:
windows 10,mysql 8,Hbuilder X(最近vs code总是崩溃)
生产环境:
windows,mysql, Nginx,Waitress

1 数据库设计

数据库名:mywork
users表结构:

DROP TABLE IF EXISTS `users`;
CREATE TABLE `users`  (`uid` int(11) NOT NULL AUTO_INCREMENT,`un` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`pwd` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`truename` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,PRIMARY KEY (`uid`) USING BTREE
) ENGINE = MyISAM AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

works表结构:

DROP TABLE IF EXISTS `works`;
CREATE TABLE `works`  (`workid` int(11) NOT NULL AUTO_INCREMENT,`date` date NULL DEFAULT NULL,`weekday` varchar(9) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`content` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL,`worktype` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`supervisetype` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`remark` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL,PRIMARY KEY (`workid`) USING BTREE
) ENGINE = MyISAM AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

2 前端实现

21.1 创建vue3 项目

npm init vue@3cd frontend[这是我的前端项目名称]
npm installnpm install axios --save
npm install element-plus --save
npm install xlsx --save

2.2 修改 index.html文件

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><link rel="icon" href="/favicon.ico"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>工作日志本</title><style>#app{background-image: url('/src/assets/meetingroom.jpg');background-size: cover;background-repeat: no-repeat;height: 98vh;width: 99vw;}</style></head><body><div id="app"></div><script type="module" src="/src/main.js"></script></body>
</html>

2.3 修改main.js文件

import './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import axios from 'axios'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import zhCn from 'element-plus/es/locale/lang/zh-cn' //设置前端时区axios.defaults.withCredentials = true  //允许跨域
axios.defaults.baseURL = 'http://127.0.0.1:8080/'  //设置后端api前缀const app = createApp(App)
app.use(ElementPlus, {locale: zhCn,})
app.use(router)
app.mount('#app')

2.4 修改App.vue文件

<script setup>
import { RouterLink, RouterView } from 'vue-router'
</script><template><RouterView />
</template><style scoped>
</style>

2.5 修改router/index.js路由配置文件

import { createRouter, createWebHistory } from 'vue-router'
import login from '../components/login.vue'
import index from '../components/index.vue'const router = createRouter({history: createWebHistory(import.meta.env.BASE_URL),routes: [{path: '/login',name: 'login',component: login},{path: '/',name: 'index',component: index},{path: '/index',name: 'index',component: index}]
})
export default router

2.6 login.vue文件(登录组件)

<template><div><div style="padding-top: 20rem;"></div><div class="loginBox" @keypress.enter="submithandle"><div style="text-align: center;"><h1>工作日志本</h1></div><div><el-form-item label="用户名"><el-input v-model="un" /></el-form-item></div><div><el-form-item label="密&emsp;码"><el-input v-model="pwd" type="password" autocomplete="off" /></el-form-item></div><div  style="text-align: center;"><el-button type="primary" round @click="submithandle">提交</el-button><el-button type="info" round>重置</el-button></div></div></div>
</template><script setup>
import { ref } from "vue";
import { useRouter } from "vue-router";
import axios from "axios";
import { ElMessage } from 'element-plus'const un = ref('')
const pwd = ref('')
const curRouter = useRouter()function submithandle() {if (un.value == "" || pwd.value == "") {ElMessage({message: '请填完整再提交。',type: 'error',})}else {axios.post("/login", { "un": un.value, "pwd": pwd.value }).then(rs => {if (rs.data.code == 200) {ElMessage({message: '登录成功!',type: 'success',})curRouter.push('/')}else {ElMessage({message: '用户名或密码不正确,请重试',type: 'error',})}})}}
</script>
<style>
.loginBox{width: 30rem;margin: 0 auto;border: 1px solid gray;border-radius: 5px;padding: 1rem;background-color: rgba(255, 255, 255, 0.8);
}
</style>

2.7 index.vue文件(核心功能组件)

<template><div><el-container><!-- 菜单栏 --><el-headerstyle="background: linear-gradient(90deg, #409eff 50%, #a12cc1 100%);border-bottom: 2px solid white;"><el-row><el-col :span="8"></el-col><el-col :span="8" style="text-align: center;color: white;"><h1>工作日志本</h1></el-col><el-col :span="8" style="text-align: right;"><el-dropdown style="line-height: 70px;"><span class="el-dropdown-link" style="color: white;">{{ truename }}<el-icon class="el-icon--right"><arrow-down /></el-icon></span><template #dropdown><el-dropdown-menu><el-dropdown-item>修改密码</el-dropdown-item><el-dropdown-item @click="logout">退出登录</el-dropdown-item></el-dropdown-menu></template></el-dropdown></el-col></el-row></el-header><!-- 工作区 --><el-main><div><el-button type="success" @click="showDraw('添加')">添加</el-button> &emsp;<el-date-picker v-model="queryformdata.querydate" type="daterange" range-separator="至" start-placeholder="开始日期"end-placeholder="结束日期" size="default"  value-format="YYYY-MM-DD" format="YYYY-MM-DD" clearable />&emsp;<el-select v-model="queryformdata.worktype" placeholder="--工作类型--" style="width: 150px" clearable><el-optionv-for="item in worktypelist" :key="item.value" :label="item.label" :value="item.value" /></el-select>&emsp;<el-select v-model="queryformdata.supervisetype" placeholder="--监督方式--" style="width: 150px" clearable><el-optionv-for="item in supervisetypelist" :key="item.value" :label="item.label":value="item.value" /></el-select>&emsp;<el-input v-model="queryformdata.content" style="width: 240px" placeholder="关键词":prefix-icon="Search" clearable/>&emsp;<el-button type="success" @click="handleQuery">查询</el-button><el-button type="success" @click="handleExport">导出</el-button><el-button type="success" @click="getTableData">全部</el-button></div><el-divider /><div><el-table:data="tableData.slice((statePager.currentPage - 1) * pagesize, statePager.currentPage * pagesize)"border style="width: 100%"><el-table-column prop="workid" label="编号" width="80" /><el-table-column prop="date" label="日期" width="150" /><el-table-column prop="weekday" label="星期" width="100" /><el-table-column prop="content" label="工作内容" width="800" /><el-table-column prop="worktype" label="工作类型" width="150" /><el-table-column prop="supervisetype" label="监督方式" width="150" /><el-table-column prop="remark" label="备注" /><el-table-column label="操作" width="200"><template #default="scope"><el-button @click="handleEdit(scope.$index, scope.row, '修改')" type="primary">修改</el-button><el-button type="danger" @click="handleDelete(scope.$index, scope.row)">删除</el-button></template></el-table-column></el-table></div><div style="background-color: white;padding-top: 1rem;"><el-pagination @current-change="handleCurrentChange" layout="total, prev, pager, next":page-size="pagesize" background :total="tableData.length"></el-pagination></div></el-main></el-container></div><!-- 编辑数据抽屉 --><el-drawer v-model="isshowdrawer" direction="rtl" size="500px"><template #header><h4>{{ acttype }}</h4></template><template #default><div><el-form :model="formData" label-width="auto" style="max-width: 600px"><el-form-item label="&emsp;&emsp;日期"><el-col><el-date-picker v-model="formData.date" type="date" placeholder="选择日期" style="width: 100%"clearable="" value-format="YYYY-MM-DD" format="YYYY-MM-DD" /></el-col></el-form-item><el-form-item label="工作内容"><el-input v-model="formData.content" type="textarea" :rows="8" /></el-form-item><el-form-item label="工作类型"><el-select v-model="formData.worktype" placeholder="--请选择--" clearable><el-optionv-for="item in worktypelist" :key="item.value" :label="item.label":value="item.value" /></el-select></el-form-item><el-form-item label="监督方式"><el-select v-model="formData.supervisetype" placeholder="--请选择--" clearable><el-optionv-for="item in supervisetypelist" :key="item.value" :label="item.label":value="item.value" /></el-select></el-form-item><el-form-item label="&emsp;&emsp;备注"><el-input v-model="formData.remark" type="text" /></el-form-item></el-form></div></template><template #footer><div style="flex: auto"><el-button @click="cancelClick">取消</el-button><el-button type="primary" @click="confirmClick">确定</el-button></div></template></el-drawer></template><script setup>// 引入功能import {ref,onMounted,reactive} from "vue";import {useRouter} from "vue-router";import axios from "axios";import {ElMessage,ElMessageBox} from 'element-plus'import {Search} from '@element-plus/icons-vue'import * as XLSX from "xlsx";// 以下为定义组件所需变量const curRouter = useRouter() //用于路由跳转的变量//接收后端传来的登录用户信息const uid = ref('')const un = ref('')const truename = ref('')//查询功能相关变量const queryformdata=reactive({querydate:'',querydatefrom:'',querydateto:'',worktype:'',supervisetype:'',content:''})// 工作类型选项const worktypelist = [{value: '111',label: '111',}, {value: '222',label: '222',}]// 工作方式选项const supervisetypelist = [{value: '111',label: '111',}, {value: '222',label: '222',}]const querykeywords = ref('')//数据表呈现const tableData = ref([]) //表格数据const statePager = reactive({currentPage: 1}); //用于保存当前表格页的变量const pagesize = ref(10) //表格中每页数据条数// 表格翻页按钮const handleCurrentChange = (e) => {statePager.currentPage = e;};//抽屉相关const isshowdrawer = ref(false) //控制抽屉是否展现,默认不展现const acttype = ref('') //记录用户打开抽屉的行为类型,拟为“添加”和“修改”const formData = reactive({workid: '',date: '',content: '',worktype: '',supervisetype: '',remark: ''}) //保存抽屉中表单数据的变量//组件加载时的动作,作用:一是检测是否登录,二是获取用户信息并拉取表格数据onMounted(() => {// 检测登录状态axios.post("login/islog").then((rs) => {// console.log(rs.data.code)if (rs.data.code != 200) {ElMessage({message: '您尚未登录,即将跳转至登录页面。',type: 'error',})curRouter.push('/login');} else {uid.value = rs.data.uid;un.value = rs.data.un;truename.value = rs.data.truename;getTableData()}})})// 退出登录function logout() {ElMessageBox.confirm('确定要退出登录吗?','Warning', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning',}).then(() => {axios.post("login/logout").then((rs) => {if (rs.data.code == 200) {curRouter.push('/login');}})}).catch(() => {})}// 拉取表格数据function getTableData() {axios.get("works").then((rs) => {tableData.value = rs.data.data;})}//查询函数function handleQuery(){if(queryformdata.querydate=='' && queryformdata.worktype=='' && queryformdata.supervisetype==''){getTableData()}else{axios.post("works/filter",{data: queryformdata}).then((rs)=>{tableData.value = rs.data.data;})}}function handleExport(){const titleArr = ['编号','日期','星期','工作内容','工作类型','工作方式','备注','操作']//表头中文名exportExcel(tableData.value, '我的工作日志', titleArr, 'sheetName');}/*把表格数据导出到excel中的函数* @description:* @param {Object} json 服务端发过来的数据* @param {String} name 导出Excel文件名字* @param {String} titleArr 导出Excel表头* @param {String} sheetName 导出sheetName名字* @return:*/function exportExcel(json, name, titleArr, sheetName) {/* convert state to workbook */var data = new Array();var keyArray = new Array();const getLength = function (obj) {var count = 0;for (var i in obj) {if (obj.hasOwnProperty(i)) {count++;}}return count;};for (const key1 in json) {if (json.hasOwnProperty(key1)) {const element = json[key1];var rowDataArray = new Array();for (const key2 in element) {if (element.hasOwnProperty(key2)) {const element2 = element[key2];rowDataArray.push(element2);if (keyArray.length < getLength(element)) {keyArray.push(key2);}}}data.push(rowDataArray);}}// keyArray为英文字段表头data.splice(0, 0, keyArray, titleArr);console.log('data', data);const ws = XLSX.utils.aoa_to_sheet(data);const wb = XLSX.utils.book_new();// 此处隐藏英文字段表头var wsrows = [{ hidden: true }];ws['!rows'] = wsrows; // ws - worksheetXLSX.utils.book_append_sheet(wb, ws, sheetName);/* generate file and send to client */XLSX.writeFile(wb, name + '.xlsx');}// 用户点击数据表中编辑按钮的事件函数,意图是把表格中的某行数据读取到表单中function handleEdit(index, row, tempacttype) {isshowdrawer.value = trueacttype.value = tempacttypeformData.workid = row.workidformData.date = row.dateformData.content = row.contentformData.worktype = row.worktypeformData.supervisetype = row.supervisetypeformData.remark = row.remark}// 用户点击数据表中删除按钮的事件函数,function handleDelete(index, row) {ElMessageBox.confirm('确定要删除编号为【' + row.workid + '】的内容吗?', 'Warning', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning',}).then(() => {axios.delete("works/" + row.workid).then((rs) => {if (rs.data.code == 200) {getTableData()}})}).catch(() => {})}//控制抽屉展现的函数function showDraw(tempacttype) {isshowdrawer.value = trueacttype.value = tempacttypeformData.workid = ''}//控制抽屉关闭的函数function cancelClick() {isshowdrawer.value = false}// 用户点击抽屉中“确定”按钮事件的函数,function confirmClick() {//如果意图是新增if (acttype.value == '添加') {axios.post('works', {data: formData}).then((rs) => {if (rs.data.code == 200) {getTableData()ElMessage({message: '添加成功,您可以继续添加。',type: 'success',})} else {ElMessage({message: '添加失败,请重试或联系开发人员。',type: 'error',})}})}// 如果意图是编辑else if (acttype.value == '修改') {axios.put('works/' + formData.workid, {data: formData}).then((rs) => {console.log(formData)if (rs.data.code == 200) {getTableData()isshowdrawer.value = falseElMessage({message: '修改成功!',type: 'success',})} else {ElMessage({message: '修改失败,请重试或联系开发人员。',type: 'error',})}})}}
</script><style scoped>.el-header {--el-header-height: 70px;}.el-divider--horizontal {margin: 0.5rem 0;}.el-table {--el-table-text-color: #000000;font-size: 16px;}
</style>

3 后端实现

后端使用Python Flask框架实现,使用Blueprint功能,开发了RESTfull风格的相关接口。

3.1 后端主入口文件app.py

from flask import Flask 
from flask import request
import pymysql
import json
from flask_cors import CORS
from flask import session
import time
import os
from flask import Blueprint, jsonify#导入两个Blueprint模块
from api.login import login
from api.works import works#设置时区
os.environ['TZ'] = 'Asia/Shanghai' app = Flask(__name__)#json数据不根据key名进行排序
app.config['JSON_SORT_KEYS'] = False # 以下代码设置程序代码更新后自动重启web服务,仅用于开发过程中。
app.debug = True
app.config.update(DEBUG=True)CORS(app, supports_credentials=True) #允许跨域访问#以下代码解决跨域时设置session值无效的问题
app.config['SESSION_COOKIE_SAMESITE'] = "None"  # 设置samesite 为None
app.config['SESSION_COOKIE_SECURE'] = True  # SECURE 为 true 
app.secret_key="guoxiyue"#注册两个Blueprint实例
app.register_blueprint(login,url_prefix='/')
app.register_blueprint(works,url_prefix='/')if __name__ == '__main__':app.run(debug=True)

3.2 api/login.py文件

from flask import Blueprint, jsonify, request,session
import pymysqldb = pymysql.connect(host="localhost", port=3306, user='root', password='123456', charset='utf8', database='mywork', cursorclass=pymysql.cursors.DictCursor) #连接数据库
mycursor = db.cursor() #创建游标对象login=Blueprint('login',__name__)# 用户登录
@login.route(rule="/login",methods=['post'])
def dologin():data=request.jsonun=data['un']pwd=data['pwd']#查询数据库sql = "select * from users where un='"+un+"' and pwd=md5('"+pwd+"')" mycursor.execute(sql)  rs = mycursor.fetchone() #处理查询结果if rs is not None:  #如果查询结果不为空,则创建sessionsession['uid']=rs['uid']session['un']=rs['un']session['truename']=rs['truename']return jsonify({'code':200,'uid':rs['uid'],'un':rs['un'],'truename':rs['truename']})else:return jsonify({'code':400})# 查询登录状态
@login.route(rule="/login/islog",methods=['post'])
def islog():un = session.get('un')if un is None:return jsonify({"code":404,"msg":"not logged"})else:return jsonify({'code':200,'uid':session.get('uid'),'un':session.get('un'),'truename':session.get('truename')})# 退出登录
@login.route(rule="/login/logout",methods=['post'])
def logout():session.clear()return jsonify({"code":200,"msg":"logout"})

3.3 api/works.py文件

from flask import Blueprint, jsonify,request
import pymysqldb = pymysql.connect(host="localhost", port=3306, user='root', password='123456', charset='utf8', database='mywork', cursorclass=pymysql.cursors.DictCursor) #连接数据库
mycursor = db.cursor() #创建游标对象
mycursor.execute("SET @@lc_time_names = 'zh_CN';")works=Blueprint('works',__name__)# 查询所有数据(即无条件查询)
@works.route(rule="/works",methods=['GET'])
def getworksList():sql = "select workid,date_format(`date`,'%Y-%m-%d') as `date`,DAYNAME(`date`) as `weekday`,content,worktype,supervisetype,remark from works order by `date` desc,workid desc" mycursor.execute(sql)  rs=mycursor.fetchall()return jsonify({"code":200,"data":rs})# 查询部分数据(即有条件查询)
@works.route(rule="/works/filter",methods=['post'])
def getFilteWorksList():filterfields=""data=request.json# print(data)if data['data']['querydate'] is not None and data['data']['querydate']!="":querydatefrom=data['data']['querydate'][0]querydateto=data['data']['querydate'][1]filterfields+=" `date` between '"+querydatefrom+"' and '"+querydateto+"' "if 'worktype' in data['data'] and data['data']['worktype'] is not None and data['data']['worktype']!="":filterfields+=" and `worktype` = '"+data['data']['worktype']+"' "if 'supervisetype' in data['data'] and data['data']['supervisetype'] is not None and data['data']['supervisetype']!="":filterfields+="and `supervisetype` = '"+data['data']['supervisetype']+"' "if data['data']['content'] is not None and data['data']['content']!="":filterfields+="and `content` like '%"+data['data']['content']+"%' "filterfields=filterfields.strip(" ") #去掉首尾空格if filterfields[0:3]=="and": filterfields=filterfields[3:] #去掉打头的andsql = "select workid,date_format(`date`,'%Y-%m-%d') as `date`,DAYNAME(`date`) as `weekday`,content,worktype,supervisetype,remark from works where "+filterfields+" order by `date` desc,workid desc" mycursor.execute(sql)  rs=mycursor.fetchall()# print(sql)return jsonify({"code":200,'data':rs})# 查询单条记录
@works.route(rule="/works/<workid>",methods=['GET'])
def getworksById(workid):# tempid = request.args.get('worksid')return jsonify({"code":200,"msg":"get workid: "+str(workid)+", OK!"})# 新增记录
@works.route(rule="/works/",methods=['POST'])
def addUser():data=request.jsondate=data['data']['date'][0:10]content=data['data']['content']worktype=data['data']['worktype']if "supervisetype" in data['data']:tempsql=data['data']['supervisetype']else:tempsql=""# supervisetype=data['data']['supervisetype']remark=data['data']['remark']sql="insert into works values(null,'"+date+"',null,'"+content+"','"+worktype+"','"+tempsql+"','"+remark+"')"# print(sql)mycursor.execute(sql)db.commit()return jsonify({"code":200,"msg":"insert works, OK!"})# 修改记录
@works.route(rule="/works/<int:workid>",methods=['PUT'])
def updateUser(workid):data=request.jsondate=data['data']['date'][0:10]content=data['data']['content']worktype=data['data']['worktype']supervisetype=data['data']['supervisetype']remark=data['data']['remark']sql="update works set `date`='"+date+"',content='"+content+"',worktype='"+worktype+"',supervisetype='"+supervisetype+"',remark='"+remark+"' where workid="+str(workid)mycursor.execute(sql)db.commit()return jsonify({"code":200,"msg":"update worksid: "+str(workid)+", OK!"})# 删除记录
@works.route(rule="/works/<int:workid>",methods=['DELETE'])
def deleteUser(workid):sql="delete from works where workid="+str(workid)mycursor.execute(sql)db.commit()return jsonify({"code":200,"msg":"delete worksid: "+str(workid)+", OK!"})

4 生产环境部署

这里采用Nginx+Waitress来实现。Nginx和Waitress的安装过程略。

4.1 编译前端项目

在前端项目根目录运行以下命令,得到编译后的项目文件,在dist目录中

npm run build

把dist目录内容复制到Nginx的www目录下即可完成前端程序的部署。

4.2 配置后端项目

在后端项目的根目录下,创建run.bat文件,内容如下:

start waitress-serve --listen=127.0.0.1:5000 app:app

双击run.bat即可运行Waitress服务。

打开nginx的配置文件,在http模块中添加以下内容:

	server{listen 8080;location / {proxy_pass http://127.0.0.1:5000;proxy_set_header Host $host;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;}}

以上代码的意思是,通过nginx服务来反向代理Waitress运行的Flask程序。即:把http://127.0.0.1:5000 访问转为http://127.0.0.1:8080,这样就实现了由Nginx来接管Flask程序的运行,但是运行Flask的Waitressp进程窗口不能关闭。重启Nginx后生效。

4.3 生产环境所需服务

一是MySQL服务,二是Nginx服务,三是Waitress进程,以上三者全部运行以后,打开浏览器,访问 http://127.0.0.1 即可运行项目。

5 运行界面截图

登录界面
数据展示界面
数据编辑界面


http://www.ppmy.cn/ops/91585.html

相关文章

【starRocks-docker 部署问题汇总】

Starrocks系列文章目录 例如&#xff1a;starrocks 常见问题汇总 文章目录 Starrocks系列文章目录前言一、Starrocks-be运行一段时间后,容器无法restart&#xff1f;二、等待后续跟新 前言 starrocks基于docker-compose部署的3节点分布式(3FE,3BE)&#xff0c;遇到的问题也都…

ubuntu2404 Server扩展PV

昨天装了一台最新版的ubuntu 服务器&#xff0c; 默认安装后发现分配的空间太少&#xff0c;1个T的硬盘只分配了100G&#xff0c;所以安装服务之前先把硬盘扩展一下。 1、查看当前的分区情况 根目录还剩90G&#xff0c;明显太小了&#xff0c;好处是用lvm管理的&#xff0c;可…

ubuntu14.04图形界面配置

Ubuntu系统启动&#xff0c;输入用户密码后&#xff0c;屏幕显示彩色背景&#xff0c;但是始终不能进入图形界面。 如果你也遇到过这种情况&#xff0c;可以参照以下方法解决&#xff08;在 ubuntu14.04 验证&#xff09;。 同时按下 alt ctrl F1&#xff0c;屏幕出现 tty1&a…

代码随想录day36 1049. 最后一块石头的重量 II 494. 目标和 474.一和零

代码随想录day36 1049. 最后一块石头的重量 II 494. 目标和 474.一和零 1049. 最后一块石头的重量 II 代码随想录 class Solution { public:int lastStoneWeightII(vector<int>& stones) {int sum 0;for (auto & ele : stones) {sum ele;}int target sum /…

C4D学习笔记

C4D学习笔记 技巧使用对称面的两边会有问题&#xff0c;很生硬洗面奶瓶盖凹槽洗面奶瓶子尾部接缝 常用功能在一个线创建一个平面在圆面内家一圈线&#xff08;KL循环切割&#xff09;在某个面向内加一圈线某个模式切换&#xff0c;选中所有(按着Ctrl键&#xff09;焊接&#xf…

聊一聊 webpack5性能优化有哪些?

介绍 此文章基于webpack5来阐述 webpack性能优化较多&#xff0c;可以对其进行分类 优化打包速度&#xff0c;开发或者构建时优化打包速度&#xff08;比如exclude、catch等&#xff09;优化打包后的结果&#xff0c;上线时的优化&#xff08;比如分包处理、减小包体积、CDN…

【c++】基础知识——快速入门c++

&#x1f31f;&#x1f31f;作者主页&#xff1a;ephemerals__ &#x1f31f;&#x1f31f;所属专栏&#xff1a;C 目录 前言 一、手搓一个Hello World 二、命名空间namespace 1.命名空间的定义 2.命名空间的使用 3.命名空间补充知识 三、c中的输入和输出 四、缺省参…

视频压缩文件太大了怎么缩小?6个视频压缩技巧,速度收藏起来!

高清视频文件&#xff0c;尤其是那些以 1080p 和 720p 清晰度为特征的视频&#xff0c;通常都拥有相当大的体积&#xff0c;会占据大量计算机存储空间。因此&#xff0c;为了更好地将它们进行分享和存储&#xff0c;您可能需要对它们进行压缩&#xff0c;以减小它们的尺寸。然而…