山科小站小程序

山科小站

山东科技大学校园小程序,如果觉得不错,点个star吧 😃
Github:https://github.com/WindrunnerMax/SHST

一、微信小程序转UNIAPP#

最近转微信小程序项目到UNIAPP项目遇到的的一些注意事项和坑
整体来说迁移项目并不是很复杂,更多的是一些重复的工作

1. 文件对应关系#

Copy
<template></template> 对应.wxml文件 <script></script> 对应.js文件 <style></style> 对应.wxss文件 在使用HBuildX创建.vue文件时会自动创建模板

2. App.vue文件#

Copy
globalData、onPageNotFound()、onLaunch()定义于此 在 onLaunch() 中直接绑定于app的方法需要操作this.$scope

3. 自定义组件#

Copy
created()方法定义为组件创建执行的方法 <slot></slot>作为组件xml插入点,可设置name属性设置多个插入点 组件单独挂载,在.vue文件引入后组件,在export default{components: {}}声明引入组件名 全局挂载组件,在main.js引入并挂载import example from "example.vue"; Vue.component('example',example);

4. 自定义文件夹#

Copy
自定义文件夹在打包时会自动编译到/common/下 静态资源放入static中,打包时目录不会变动,wxml引入目录不变

5. 数据绑定#

Copy
微信小程序中使用 setData({}) 方法将数据发送到视图层 Vue中数据双向绑定,使用 this.param = value 重新渲染数据, 当然也可以重写 setData({}) 方法,官网给予示例 setData:function(obj){ let that = this; let keys = []; let val,data; Object.keys(obj).forEach(function(key){ keys = key.split('.'); val = obj[key]; data = that.$data; keys.forEach(function(key2,index){ if(index+1 == keys.length){ that.$set(data,key2,val); }else{ if(!data[key2]){ that.$set(data,key2,{}); } } data = data[key2]; }) }); }

6. template数据渲染#

Copy
要使用的数据必须首先在export default {data() {return { param : value}}}中声明 在xml节点属性中使用:attr引用变量值,在xml节点的值使用{{param}} 循环wx:for="{{list}}" wx:key="{{index}}"> 改为 v-for="(item,index) in list" :key="index"

7. 动态绑定class与style#

Copy
在ES6中可直接在:attr中使用新特性`string${param}string`来拼接字符串,但是微信小程序还不支持 手动拼接字符串的方式:attr="'string' + param" Vue中提供了动态绑定的方式 <view class="static" :class="{value:active}" :style="{'attr': param}"></view>

8. page.json#

Copy
在微信小程序的app.json路由在Unaipp中同样由page.json文件统一管理 微信小程序时在app.json中写入路由则创建文件,HbuildX中在创建页面时可选自动在page.json创建路由 在page.json中style对应微信小程序某一页面的json文件

9. 条件编译功能#

Copy
// #ifdef %PLATFORM% 平台特有的API实现,UNIAPP提供的条件编译功能,非常适用于跨端开发 // #endif

10. 阿里矢量图标库Iconfont#

Copy
将图标添加到项目,以代码的方式下载到本地 复制iconfont.css到项目,移除所有类似 url('iconfont.eot?t=1577846073653#iefix') format('embedded-opentype') 部分 在<view class='iconfont icon-shuaxin'></view>引用即可

二、山科小站实例#

1. 目录结构#

Copy
SHST-UNI // 山科小站总目录 ├── components // 组件封装 │ ├── headslot.vue // 带solt的标题布局 │ ├── layout.vue // 卡片式布局 │ ├── list.vue // 展示用list布局 │ ├── sentence.vue // 每日一句封装 │ └── weather.vue // 天气封装 ├── modules // 模块化封装 │ ├── cookies.js // Cookies操作 │ ├── copy.js // 深浅拷贝 │ ├── datetime.js // 时间日期操作 │ ├── event-bus.js // 事件总线 │ ├── global-data.js // 全局变量 │ ├── loading.js // 加载提示 │ ├── operate-limit.js // 防抖与节流 │ ├── regex.js // 正则匹配 │ ├── request.js // 网络请求 │ ├── toast.js // 消息提示 │ └── update.js // 自动更新 ├── pages // 页面 │ ├── Ext // 拓展组 │ ├── Home // Tabbar、辅助组 │ ├── Lib // 图书馆功能组 │ ├── Sdust // 科大组 │ ├── Study // 学习组 │ └── User // 用户组 ├── static // 静态资源 │ ├── camptour // 校园导览静态资源 │ └── img // 图标等静态资源 ├── unpackage // 打包文件 ├── utils // 辅助功能 │ ├── amap-wx.js // 高德地图SDK │ └── md5.js // MD5引入 ├── vector // 部署封装 │ ├── resources // 资源文件 │ │ ├── camptour // 校园导览配置文件 │ │ ├── asse.mini.wxss // 公共样式库 │ │ └── iconfont.wxss // 字体图标 │ ├── dispose.js // 部署小程序 │ └── pubFct.js // 公有方法 ├── App.vue // App全局样式以及监听 ├── main.js // 挂载App,Vue初始化入口文件 ├── manifest.json // 配置Uniapp打包等信息 ├── pages.json // 路由 └── uni.scss // 内置的常用样式变量

三、模块化#

1. Cookies操作#

Copy
/** * GetCookie */ function getCookies(res) { var cookies = ""; if (res && res.header && res.header['Set-Cookie']) { // #ifdef MP-ALIPAY var cookies = res.header['Set-Cookie'][0].split(";")[0] + ";"; // #endif // #ifndef MP-ALIPAY var cookies = res.header['Set-Cookie'].split(";")[0] + ";"; // #endif console.log("SetCookie:" + cookies); uni.setStorage({ key: "cookies", data: cookies }); } else { console.log("Get Cookie From Cache"); cookies = uni.getStorageSync("cookies") || ""; } return cookies; } export { getCookies } export default { getCookies }

2. 深浅拷贝#

Copy
function shallowCopy(target, ...origin) { return Object.assign(target, ...origin); } function extend(target, ...origin) { return shallowCopy(target, ...origin); } function deepCopy(target, origin) { for (let item in origin) { if (origin[item] && typeof(origin[item]) === "object") { // Object Array Date RegExp 深拷贝 if (Object.prototype.toString.call(origin[item]) === "[object Object]") { target[item] = deepCopy({}, origin[item]); } else if (origin[item] instanceof Array) { target[item] = deepCopy([], origin[item]); } else if (origin[item] instanceof Date) { target[item] = new Date(origin[item]); } else if (origin[item] instanceof RegExp) { target[item] = new RegExp(origin[item].source, origin[item].flags); } else { target[item] = origin[item]; } } else { target[item] = origin[item]; } } return target; } export { extend, shallowCopy, deepCopy } export default { extend, shallowCopy, deepCopy }

3. 时间日期操作#

Copy
/** * yyyy年 MM月 dd日 hh1~12小时制(1-12) HH24小时制(0-23) mm分 ss秒 S毫秒 K周 */ const formatDate = (fmt = "yyyy-MM-dd", date = new Date()) => { var week = ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"]; var o = { "M+": date.getMonth() + 1, //月份 "d+": date.getDate(), //日 "h+": date.getHours(), //小时 "m+": date.getMinutes(), //分 "s+": date.getSeconds(), //秒 "q+": Math.floor((date.getMonth() + 3) / 3), //季度 "S": date.getMilliseconds(), //毫秒 "K": week[date.getDay()] }; if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length)); for (var k in o) { if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (( "00" + o[k]).substr(("" + o[k]).length))); } return fmt; } const extDate = () => { // console.log("拓展Date原型"); Date.prototype.addDate = function(years = 0, months = 0, days = 0) { if (days !== 0) this.setDate(this.getDate() + days); if (months !== 0) this.setMonth(this.getMonth() + months); if (years !== 0) this.setFullYear(this.getFullYear() + years); } } /** * 日期相差天数 */ const dateDiff = (startDateString, endDateString) => { var separator = "-"; //日期分隔符 var startDates = startDateString.split(separator); var endDates = endDateString.split(separator); var startDate = new Date(startDates[0], startDates[1] - 1, startDates[2]); var endDate = new Date(endDates[0], endDates[1] - 1, endDates[2]); var diff = parseInt((endDate - startDate) / 1000 / 60 / 60 / 24); //把相差的毫秒数转换为天数 return diff; } export { formatDate, extDate, dateDiff } export default { formatDate, extDate, dateDiff }

4. 事件总线#

Copy
var PubSub = function() { this.handlers = {}; } PubSub.prototype = { on: function(key, handler) { // 订阅 if (!(key in this.handlers)) this.handlers[key] = []; this.handlers[key].push(handler); }, off: function(key, handler) { // 卸载 const index = this.handlers[key].findIndex(item => item === handler); if (index < 0) return false; if (this.handlers[key].length === 1) delete this.handlers[key]; else this.handlers[key].splice(index, 1); return true; }, commit: function(key, ...args) { // 触发 if (!this.handlers[key]) return false; this.handlers[key].forEach(handler => handler.apply(this, args)); return true; }, } export { PubSub } export default { PubSub }

5. 全局变量#

Copy
/** * 颜色方案 */ // var colorList = ["#EAA78C", "#F9CD82", "#9ADEAD", "#9CB6E9", "#E49D9B", "#97D7D7", "#ABA0CA", "#9F8BEC", // "#ACA4D5", "#6495ED", "#7BCDA5", "#76B4EF","#E1C38F","#F6C46A","#B19ED1","#F09B98","#87CECB","#D1A495","#89D196" // ]; var colorList = ["#FE9E9F", "#93BAFF", "#D999F9", "#81C784", "#FFCA62", "#FFA477"]; export { colorList } export default { colorList }

6. 加载提示#

Copy
/** * startLoading */ function startLoading(option) { switch (option.load) { case 1: uni.showNavigationBarLoading(); break; case 2: uni.showNavigationBarLoading(); uni.setNavigationBarTitle({ title: option.title || "加载中..." }) break; case 3: uni.showLoading({ title: option.title || "请求中", mask: true }) break; } } /** * endLoading */ function endLoading(option) { switch (option.load) { case 1: uni.hideNavigationBarLoading(); break; case 2: uni.hideNavigationBarLoading(); uni.setNavigationBarTitle({ title: option.title || "山科小站" }) break; case 3: uni.hideLoading(); break; } } export { startLoading, endLoading } export default { startLoading, endLoading }

7. 防抖与节流#

Copy
/** * 防抖 * 定时器实现 */ function debounceGenerater(){ var timer = null; return (wait, funct, ...args) => { clearTimeout(timer); timer = setTimeout(() => funct(...args), wait); } } /** * 节流 * 时间戳实现 */ function throttleGenerater(){ var previous = 0; return (wait, funct, ...args) => { var now = +new Date(); if(now - previous > wait){ funct(...args); previous = now; } } } /* // 节流 // 定时器实现 function throttleGenerater(){ var timer = null; return (wait, funct, ...args) => { if(!timer){ funct(...args); timer = setTimeout(() => timer = null, wait); } } } */ export { debounceGenerater, throttleGenerater } export default { debounceGenerater, throttleGenerater }

8. 正则匹配#

Copy
/** * 正则匹配 */ const regMatch = (regex, s) => { var result = []; var temp = null; var flags = `${regex.flags}${regex.flags.includes("g") ? "" : "g"}`; regex = new RegExp(regex, flags); while (temp = regex.exec(s)) result.push(temp[1] ? temp[1] : temp[0]); return result; } export { regMatch } export default { regMatch }

9. 网络请求#

Copy
import {startLoading, endLoading} from "./loading"; import {getCookies} from "./cookies"; import {extend} from "./copy"; import {toast} from "./toast"; var headers = {'content-type': 'application/x-www-form-urlencoded'}; /** * HTTP请求 */ function ajax(requestInfo) { var option = { load: 1, url: "", method: "GET", data: {}, headers: headers, success: () => {}, resolve: () => {}, fail: function() { this.completeLoad = () => { toast("External Error");}}, reject: () => {}, complete: () => {}, completeLoad: () => {} }; extend(option, requestInfo); startLoading(option); uni.request({ url: option.url, data: option.data, method: option.method, header: headers, success: function(res) { if (!headers.cookie) headers.cookie = getCookies(res); if(res.statusCode === 200){ try { option.success(res); option.resolve(res); } catch (e) { option.completeLoad = () => { toast("External Error");} console.log(e); } }else{ option.fail(res); option.reject(res); } }, fail: function(res) { option.fail(res); }, complete: function(res) { endLoading(option); try { option.complete(res); } catch (e) { console.log(e); } option.completeLoad(res); } }) } /** * request promise封装 */ function request(option) { return new Promise((resolve,reject) => { option.resolve = resolve; option.reject = reject; ajax(option); }) } export { ajax, request } export default { ajax, request }

10. 消息提示#

Copy
/** * 弹窗提示 */ function toast(e, time = 2000, icon = 'none') { uni.showToast({ title: e, icon: icon, duration: time }) } export { toast } export default { toast }

11. 自动更新#

Copy
/** * 小程序更新 */ function checkUpdate() { if (!uni.getUpdateManager) return false; uni.getUpdateManager().onCheckForUpdate((res) => { console.log("Update:" + res.hasUpdate); if (res.hasUpdate) { //如果有新版本 uni.getUpdateManager().onUpdateReady(() => { //当新版本下载完成 uni.showModal({ title: '更新提示', content: '新版本已经准备好,单击确定重启应用', success: (res) => { if (res.confirm) uni.getUpdateManager().applyUpdate(); //applyUpdate 应用新版本并重启 } }) }) uni.getUpdateManager().onUpdateFailed(() => { //当新版本下载失败 uni.showModal({ title: '提示', content: '检查到有新版本,但下载失败,请检查网络设置', showCancel: false }) }) } }) } export { checkUpdate } export default { checkUpdate }

12. 启动事件#

Copy
"use strict"; import globalData from "@/modules/global-data"; import request from "@/modules/request"; import {toast} from "@/modules/toast"; import {extend} from "@/modules/copy"; import {PubSub} from "@/modules/event-bus"; import {extDate} from "@/modules/datetime"; import {checkUpdate} from "@/modules/update"; import {getCurWeek} from "@/vector/pubFct"; function disposeApp(app){ extDate(); //拓展Date原型 checkUpdate(); // 检查更新 app.$scope.toast = toast; app.$scope.extend = extend; app.$scope.eventBus = new PubSub(); app.$scope.extend(app.$scope, request); app.$scope.extend(app.globalData, globalData); app.globalData.colorN = app.globalData.colorList.length; app.globalData.curWeek = getCurWeek(app.globalData.curTermStart); } /** * APP启动事件 */ function onLaunch() { var app = this; disposeApp(this); var userInfo = uni.getStorageSync("user") || {}; uni.login({ scopes: 'auth_base' }).then((data) => { var [err,res] = data; if(err) return Promise.reject(err); return app.$scope.request({ load: 3, // #ifdef MP-WEIXIN url: app.globalData.url + 'auth/wx', // #endif // #ifdef MP-QQ url: app.globalData.url + 'auth/QQ', // #endif method: 'POST', data: { "code": res.code, user: JSON.stringify(userInfo) } }) }).then((res) => { app.globalData.curTerm = res.data.initData.curTerm; app.globalData.curTermStart = res.data.initData.termStart; app.globalData.curWeek = res.data.initData.curWeek; app.globalData.loginStatus = res.data.Message; app.globalData.initData = res.data.initData; if(app.globalData.initData.custom){ let custom = app.globalData.initData.custom; if(custom.color_list) { app.globalData.colorList = JSON.parse(custom.color_list); app.globalData.colorN = app.globalData.colorList.length; } } if (res.data.Message === "Ex") app.globalData.userFlag = 1; else app.globalData.userFlag = 0; console.log("Status:" + (app.globalData.userFlag === 1 ? "User Login" : "New User")); if (res.data.openid) { var notify = res.data.initData.tips; app.globalData.tips = notify; var point = uni.getStorageSync("point") || ""; if (point !== notify) uni.showTabBarRedDot({ index: 2 }); console.log("SetOpenid:" + res.data.openid); app.globalData.openid = res.data.openid; uni.setStorageSync('openid', res.data.openid); } else { console.log("Get Openid From Cache"); app.globalData.openid = uni.getStorageSync("openid") || ""; } return Promise.resolve(res); }).then((res) => { if (res.statusCode !== 200 || !res.data.initData || !res.data.initData.curTerm) return Promise.reject("DATA INIT FAIL"); else app.$scope.eventBus.commit('LoginEvent', res); }).catch((err) => { console.log(err); uni.showModal({ title: '警告', content: '数据初始化失败,点击确定重新初始化数据', showCancel: false, success: (res) => { if (res.confirm) onLaunch.apply(app); } }) }) } export default {onLaunch, toast}

四、组件化#

1. 标题组件#

Copy
<template name="headslot"> <view> <view class="head-line"> <view class="head-left"> <view class="head-row" v-bind:style="{'background-color': color}"></view> <view class="head-title">{{title}}</view> </view> <view style="margin-top: 3px;"> <slot></slot> </view> </view> </view> </template> <script> export default { name: "headslot", props: { title: { type: String }, color: { type: String, default: "#79B2F9" }, }, methods: {} } </script> <style> .head-line { background-color: #FFFFFF; padding: 10px 5px; box-sizing: border-box; display: flex; border-bottom: 1px solid #EEEEEE; justify-content: space-between; } .head-row { width: 2px; margin: 2px 5px; } .head-left{ display: flex; justify-content: center; } .head-title{ display: flex; justify-content: center; align-items: center; white-space: nowrap; } </style>

2. 卡片组件#

Copy
<template> <view> <view class="lay-line" v-show="title"> <view class="lay-left-con"> <view class="lay-verline" :style="{background: color}"></view> <view>{{title}}</view> </view> <view> <slot name="headslot"></slot> </view> </view> <view class="lay-card" :style="{color: computedColor}" :class="{'lay-min-top':!topSpace}"> <slot></slot> </view> </view> </template> <script> export default { name: "layout", props: { title: { type: String, required: true }, color: { type: String, default: "#79B2F9" }, topSpace: { type: Boolean, default: true }, inheritColor: { type: Boolean, default: false } }, computed:{ computedColor: function(){ return this.inheritColor ? this.color : "unset"; } }, methods: {} } </script> <style> .lay-line { background-color: #FFFFFF; padding: 12px 5px 10px 5px; box-sizing: border-box; display: flex; border-bottom: 1px solid #EEEEEE; justify-content: space-between; align-items: center; } .lay-verline { width: 2px; margin: 2px 5px; } .lay-verline + view{ color: #000000; } .lay-card { font-size: 13px; background-color: #FFFFFF; padding: 11px; box-sizing: border-box; margin-bottom: 10px; } .lay-min-top { padding-top: 3px; } .lay-left-con { display: flex; } </style>

3. 列表组件#

Copy
<template name="list"> <view> <view class="list-line"> <view class="list-left" v-bind:style="{'background-color': color}"></view> <view class="list-right">{{title}}</view> </view> <view v-for="(item,index) in info" :key='index'> <view class='list-card'> <text >{{item}}</text> </view> </view> </view> </template>S <script> export default { name: "list", props: { title: { type: String, default: "Default" }, color: { type: String, default: "#79B2F9" }, info: { type: Array } }, methods: {} } </script> <style> .list-line{ background-color: #FFFFFF; padding:10px 5px; box-sizing: border-box; display: flex; margin-bottom: 10px; } .list-line .list-left{ width: 2px; margin: 2px 5px; } .list-card{ font-size: 13px; background-color: #FFFFFF; padding: 15px; box-sizing: border-box; margin-bottom: 10px; } </style>

4. 每日一句组件#

Copy
<template name="sentence"> <view> <view style="margin: 6px 0 8px 3px;">{{sentence}}</view> <view style="margin: 3px 0 8px 3px;">{{content}}</view> <image class="sent-image" :src="url" mode="aspectFill"></image> </view> </template> <script> export default { name: "sentence", props: {}, methods: {}, data() { return { url: "", sentence: "", content: "" } }, created: function() { var that = this; uni.request({ url: "https://open.iciba.com/dsapi/", success: function(res) { that.url = res.data.picture2; that.sentence = res.data.note; that.content = res.data.content; } }) } } </script> <style> .sent-image { width: 100%; } </style>

5. 天气组件#

Copy
<template name="weather"> <view> <view class='weather'> <view class='weaLeft'> <view style="display: flex;align-items: center;justify-content: center;"> <image class='todayImg' mode="aspectFit" :src="host+'/public/static/weather/'+todayWeather[1]+'.png'"></image> </view> <view style='text-align:center;margin-top:6px;'>{{todayWeather[0]}}</view> <view style='text-align:center;margin-top:3px;'>{{todayWeather[2]}}℃ - {{todayWeather[3]}}℃</view> <view style='text-align:center;margin-top:3px;'>{{todayWeather[4]}}</view> </view> <view class='weaRight'> <view class='weaRightTop'> <image class='dayImg' mode="aspectFit" :src="host+'/public/static/weather/'+tomorrowWeather[1]+'.png'"></image> <view class='weatherCon'> <view style='text-align:center;margin-top:6px;'>{{tomorrowWeather[0]}}</view> <view style='text-align:center;margin-top:3px;'>{{tomorrowWeather[2]}}℃ - {{tomorrowWeather[3]}}℃</view> </view> </view> <view class='weaRightBot'> <image class='dayImg' mode="aspectFit" :src="host+'/public/static/weather/'+tdatomoWeather[1]+'.png'"></image> <view class='weatherCon'> <view style='text-align:center;margin-top:3px;'>{{tdatomoWeather[0]}}</view> <view style='text-align:center;'>{{tdatomoWeather[2]}}℃ - {{tdatomoWeather[3]}}℃</view> </view> </view> </view> </view> </view> </template> <script> export default { name: "weather", props: {}, methods: {}, data() { return { todayWeather: ["", "CLEAR_DAY", 0, 0, "数据获取中"], tomorrowWeather: ["", "CLEAR_DAY", 0, 0], tdatomoWeather: ["", "CLEAR_DAY", 0, 0], host: "https://www.touchczy.top" } }, created: function() { var that = this; var ran = parseInt(Math.random() * 100000000000); uni.request({ url: "https://api.caiyunapp.com/v2/Y2FpeXVuIGFuZHJpb2QgYXBp/120.127164,36.000129/weather?lang=zh_CN&device_id=" + ran, success: function(res) { if (res.data.status === "ok") { var weatherData = res.data.result.daily; that.todayWeather = [weatherData.skycon[0].date, weatherData.skycon[0].value, weatherData.temperature[0].min, weatherData.temperature[0].max, res.data.result.hourly.description ] that.tomorrowWeather = [weatherData.skycon[1].date, weatherData.skycon[1].value, weatherData.temperature[1].min, weatherData.temperature[1].max ] that.tdatomoWeather = [weatherData.skycon[2].date, weatherData.skycon[2].value, weatherData.temperature[2].min, weatherData.temperature[2].max ] } } }) } } </script> <style> .weather { display: flex; border: 1px solid #eee; transition: all 0.8s; font-size: 13px; border-radius: 3px; border-bottom-left-radius: 0; border-bottom-right-radius: 0; } .weaLeft { width: 50% ; padding: 10px; border-right: 1px solid #eee; } .todayImg { width: 40px !important; height: 40px !important; } .dayImg { width: 30px !important; height: 30px !important; margin: 0 0 0 15px; align-self: center; } .weaRight { width: 50%; } .weaRightBot, .weaRightTop { display: flex; height: 50%; text-align: center; } .weaRightBot { border-top: 1px solid #eee; } .weatherCon { align-self: center; margin: 0 auto; } </style>

五、小程序部署#

1. 初始化小程序#

Copy
import dispose from "@/vector/dispose"; export default { globalData: { tips: "0", openid: "", userFlag: 0, // 0 未登录 1 已登陆 initData: {}, version: "3.3.0", curTerm: "2019-2020-1", curTermStart: "2019-08-26", url: 'https://www.touchczy.top/', // url: 'http://dev.touchczy.top/', }, onPageNotFound: (res) => { //处理404 uni.reLaunch({ url: 'pages/Home/auxiliary/notFound' }) }, onLaunch: function() { console.log("APP INIT"); dispose.onLaunch.apply(this); //启动加载事件 }, onError: (err) => { console.log(err); dispose.toast("Internal Error"); } }

2. 全局样式#

Copy
/*全局样式*/ @import "@/vector/resources/asse.mini.wxss"; @import "@/vector/resources/iconfont.wxss"; button:after { border: none; } button { background: #fff; border: none; box-sizing: unset; padding: 0; margin: 0; font-size: 13px; line-height: unset; height: auto; } .adapt{ box-sizing: border-box; } .tipsCon view{ padding: 5px; }
posted @   WindRunnerMax  阅读(1328)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示
CONTENTS