uniapp实战教程:如何封装一个可复用的表单组件

news2024/9/29 13:53:50

在uniapp开发过程中,表单组件的使用场景非常广泛。为了提高开发效率,我们可以将常用的表单组件进行封装。本文将带你了解如何在uniapp中封装一个表单组件,让你只需要通过属性配置轻松实现各种表单,效果图如下:

一、准备工作

在开始封装表单组件之前,请确保你已经掌握了以下知识:

        1、uniapp基础知识

        2、Vue.js基础知识

        3、组件通信与传值

二、分析需求

在封装表单组件之前,我们需要明确以下需求:

        1、支持多种表单元素(如:输入框、可下拉选择输入框、时间选择器、数字加减器、图片上传、下拉框等)

        2、支持自定义确认和取消按钮

        3、支持表单验证

        4、支持添加插槽

        5、支持表单数据提交

三、封装步骤

1、在uniapp项目的components目录下,创建一个名为customForm的文件夹,并在该文件夹下创建index.vue文件,文件代码如下:
<template>
	<view>
		<form @submit="formSubmit" @reset="formReset">
			<view v-for="(item, idx) in formConfig" :key="item.name"
				class="flex w-p-100 align-center row fz-17 relative">
				<view v-if="item.label" class="w-p-30">{{item.label}}</view>
				<view v-if="item.require" class="inline-block absolute t-15 l-9 fz-18 text-red">*</view>
				<!-- 输入框 -->
				<input class="flex1 form-input" v-if="item.type=='input'" :placeholder="item.placeholder" :name="idx"
					v-model="data[idx]" :disabled="item.disabled" :type="item.inputType || 'text'">
				<!-- 下拉选择框 -->
				<picker v-else-if="item.type=='picker'" @change="bindPickerChange" :data-index="idx"
					:value="item.selectedIndex" :range="item.arrayData" :range-key="item.rangeKey" :name="idx">
					<input :value="item.rangeKey ? item.arrayData[item.selectedIndex]&&item.arrayData[item.selectedIndex][item.rangeKey] : item.arrayData[item.selectedIndex]"  disabled :placeholder="item.placeholder" class="uni-input" />
				</picker>
				<!-- 时间选择 -->
				<datePicker v-else-if="item.type=='datePicker'" :timeFormat="item.timeFormat"
					@datetimeChange="e => datetimeChange(e,idx)"></datePicker>
				<!-- 图片上传 -->
				<view v-else-if="item.type=='upLoad'" class="file-picker">
					<uni-file-picker :limit="item.limit" @select="e => handleSelect(e, item, idx)" @delete="e => deletePictrue(e, idx)"
						:autoUpload="false" :value="data[idx]?[{url:data[idx]}]:''"></uni-file-picker>
				</view>
				<!-- 可下拉选择输入框 -->
				<input-select class="flex1 fz-18" v-else-if="item.type == 'inputSelect'" :placeholder="item.placeholder"
					:options="item.options" :value="data[idx]" @change="e => inputSelectChange(e, idx)"></input-select>
				<!-- 数字加减器 -->
				<view class="flex1 h-p-100 flex align-center" v-else-if="item.type == 'numberBox'">
					<uni-number-box @change="e => bindNumberChange(e, idx)" class="uni-number-box" :min="1"
						v-model="data[idx]" />
				</view>
				<!-- 插槽 -->
				<slot v-else-if="item.type == 'slot'" :name="item.slotName"></slot>
			</view>
			<view class="p-15">
				<button form-type="submit" type="primary">{{submitTxet}}</button>
				<button v-if="reset" form-type="reset">{{resetText}}</button>
			</view>
		</form>
	</view>
</template>

<script setup>
	import datePicker from '../datePicker/datePicker.vue';
	import {
		reactive,
		ref,
		watch,
		toRefs
	} from 'vue'
	import * as utils from '@/utils/index.js'
	const props = defineProps({
		reset: {
			type: Boolean,
			default: false
		},
		resetTxet: {
			type: String,
			default: '重置'
		},
		submitTxet: {
			type: String,
			default: '提交'
		},
		formConfig: {
			type: Object,
			required: true,
			default: () => {
				return {}
			}
		},
		resultData: {
			type: Object,
			default: () => {
				return {}
			}
		}
	})

	let data = reactive(props.resultData)
	// const pickerValue = ref('')
	const bindPickerChange = (e) => {
		let index = e.detail.value,
			idx = e.target.dataset.index,
			item = props.formConfig[idx]
		item.selectedIndex = index
		data[idx] = item.rangeKey ? item.arrayData[index][item.key || 'id'] : item.arrayData[index]

	}
	const handleSelect = (e, item, idx) => {
		if (item.success) {
			item.success(e, idx)
		} else {
			uploadSuccess(e, idx)
		}
	}
	const uploadSuccess = (e, idx) => {
		data[idx] = e.tempFilePaths[0]
	}
	
	const deletePictrue = (e, idx) => {
		data[idx] = ''
	}
	const datetimeChange = (e, idx) => {
		data[idx] = e.detail.valueStr
	}
	const inputSelectChange = (e, idx) => {
		data[idx] = e.detail.value
	}
	const bindNumberChange = (e, idx) => {
		data[idx] = e
	}
	const emit = defineEmits(['formSubmit'])
	const formSubmit = (e) => {
		let bool = utils.formVerify(data, props.formConfig)
		bool && emit('formSubmit', data)

	}
</script>

<style>
	.row {
		min-height: 90rpx;
		padding: 0 0 0 40rpx;
		box-sizing: border-box;
		border-bottom: 1px solid #ccc;
	}

	.file-picker {
		width: 264rpx;
	}
	
	.form-input {
		height: 90rpx;
	}
</style>
2、在父组件中使用:

父组件.vue文件中:

<template>
	<view>
		<customForm :formConfig="fromConfigRef" :resultData="resultData" submitTxet="确定" @formSubmit="formSubmit"></customForm>
	</view>
</template>

<script setup>
	import {
		ref,
		reactive
	} from 'vue'
	import {
		fromConfig
	} from './fromConfig';
import {
		onLoad
	} from '@dcloudio/uni-app'
	import API from '@/api/index.js'
	const fromConfigRef = reactive(fromConfig)
	const resultData = reactive({})
	let statusIndex = null
	onLoad(async(options) => {
		if(options.resultData) {
			let data = JSON.parse(decodeURIComponent(options.resultData)) 
			        // 更新响应式对象resultData的属性
			        for (const key in data) {
			          resultData[key] = data[key];
			        }
			
		}
		statusIndex = options.statusIndex
		let res = await API.SiteOrder.pullDownInstrumentName()
		fromConfigRef['factoryName'].options = res
		// console.log(fromConfigRef)
	})
	const formSubmit = (data) => {
		if(!data.number) {
			data.number = 1
		}
		const backData = {
			statusIndex,
			data
			
		}
		uni.navigateBack({
			delta: 1,
			success: () => {
				uni.$emit('pushData', backData)
			}

		})
	}
</script>

<style>

</style>

fromConfigRef.js配置文件:

export const fromConfig = {
	'factoryName':{
		label:'器具名称',
		type: 'inputSelect',
		placeholder: '请输入器具名称',
		options:[],
		require: true
	},
	'factoryFormat':{
		label:'器具规格',
		type: 'input',
		placeholder: '请输入器具规格',
		require: true
	},
	'factoryNo':{
		label:'器具编号',
		type: 'input',
		placeholder: '请输入器具编号'
	},
	'number':{
		label:'数量',
		type: 'numberBox'
	},
	'person':{
		label:'联系人',
		type: 'input',
		placeholder: '请输入联系人',
		require: true
	},
	'marks':{
		label:'备注',
		type: 'input',
		placeholder: '请输入备注',
	},
}

 

 

3、关于customForm组件的index.vue文件,有以下几点需要注意:
1、class样式

我采用了原子化css样式,所以在这个文件style中并没有太多的 样式 ,而是直接用了原子化css里面的class名,比如:class="flex",表示display:flex。原子化css文件已给出。

2、自定义组件

组件中datePicker、input-select为另外封装的自定义组件,主要实现了日期时间选择和可输入可选择下拉框。

datePicker.vue组件文件如下,如需要可自取:

<template>
    <view style="height: 100%">
        <picker mode="multiSelector" :value="dateTime" @change="changeDateTime" @columnchange="changeDateTimeColumn" :range="dateTimeArray">
            <view class="weui-input">
                <block v-if="timeFormat == 'YYYY-MM-DD HH:mm'">
                    {{ dateTimeArray && dateTimeArray[0][dateTime[0]] }}-{{ dateTimeArray && dateTimeArray[1][dateTime[1]] }}-{{ dateTimeArray && dateTimeArray[2][dateTime[2]] }} {{ dateTimeArray && dateTimeArray[3][dateTime[3]] }}:{{
                        dateTimeArray && dateTimeArray[4][dateTime[4]]
                    }}
                </block>
                <block v-else>
                    {{ dateTimeArray && dateTimeArray[0][dateTime[0]] }}-{{ dateTimeArray && dateTimeArray[1][dateTime[1]] }}-{{ dateTimeArray && dateTimeArray[2][dateTime[2]] }} {{ dateTimeArray && dateTimeArray[3][dateTime[3]] }}:{{
                        dateTimeArray && dateTimeArray[4][dateTime[4]]
                    }}:{{ dateTimeArray && dateTimeArray[5][dateTime[5]] }}
                </block>
            </view>
        </picker>
    </view>
</template>

<script>
import * as utils from '@/utils/index.js'
export default {
	name:'datePicker',
    data() {
        return {
            dateTimeArray: null,
            //时间年月日时分秒数组
            dateTime: null,
            //选中的年月日时分秒每个数组的下标
            startYear: 1900,
            //起始年份
            endYear: 2200 //结束年份
        };
    },
    /**
     * 组件的属性列表
     */
    props: {
        value: {
            type: String,
            default: ''
        },
        //默认值,不传为当前时间
        timeFormat: {
            type: String,
            default: 'YYYY-MM-DD HH:mm:ss'
        } //时间格式 YYYY-MM-DD HH:mm:ss 和 YYYY-MM-DD HH:mm两种
    },
    /**
     * 组件的方法列表
     */
    methods: {
        attached() {
            //初始化
            var obj = utils.dateTimePicker(this.startYear, this.endYear, this.value);
            if (this.timeFormat == 'YYYY-MM-DD HH:mm') {
                //如果是精准到分,则去掉分的数据
                obj.dateTimeArray.pop();
            }
			this.dateTime = obj.dateTime;
			this.dateTimeArray = obj.dateTimeArray
            //将初始化后的时间值返回给绑定的value
            let dateTime = '';
            let dateTimeStr = '';
            if (this.timeFormat == 'YYYY-MM-DD HH:mm') {
                dateTime =
                    this.dateTimeArray[0][this.dateTime[0]] +
                    '-' +
                    this.dateTimeArray[1][this.dateTime[1]] +
                    '-' +
                    this.dateTimeArray[2][this.dateTime[2]] +
                    ' ' +
                    this.dateTimeArray[3][this.dateTime[3]] +
                    ':' +
                    this.dateTimeArray[4][this.dateTime[4]];
                dateTimeStr =
                    this.dateTimeArray[0][this.dateTime[0]] +
                    '-' +
                    this.dateTimeArray[1][this.dateTime[1]] +
                    '-' +
                    this.dateTimeArray[2][this.dateTime[2]] +
                    'T' +
                    this.dateTimeArray[3][this.dateTime[3]] +
                    ':' +
                    this.dateTimeArray[4][this.dateTime[4]] +
                    ':00.000Z';
            } else {
                dateTime =
                    this.dateTimeArray[0][this.dateTime[0]] +
                    '-' +
                    this.dateTimeArray[1][this.dateTime[1]] +
                    '-' +
                    this.dateTimeArray[2][this.dateTime[2]] +
                    ' ' +
                    this.dateTimeArray[3][this.dateTime[3]] +
                    ':' +
                    this.dateTimeArray[4][this.dateTime[4]] +
                    ':' +
                    this.dateTimeArray[5][this.dateTime[5]];
                dateTimeStr =
                    this.dateTimeArray[0][this.dateTime[0]] +
                    '-' +
                    this.dateTimeArray[1][this.dateTime[1]] +
                    '-' +
                    this.dateTimeArray[2][this.dateTime[2]] +
                    'T' +
                    this.dateTimeArray[3][this.dateTime[3]] +
                    ':' +
                    this.dateTimeArray[4][this.dateTime[4]] +
                    ':' +
                    this.dateTimeArray[5][this.dateTime[5]] +
                    '.000Z';
            }
            this.$emit('datetimeChange', {
                detail: {
                    value: dateTime,
                    valueStr: dateTimeStr
                }
            });
        },

        changeDateTime(e) {
			this.dateTime = e.detail.value
            let dateTime = '';
            let dateTimeStr = '';
            if (this.timeFormat == 'YYYY-MM-DD HH:mm') {
                dateTime =
                    this.dateTimeArray[0][this.dateTime[0]] +
                    '-' +
                    this.dateTimeArray[1][this.dateTime[1]] +
                    '-' +
                    this.dateTimeArray[2][this.dateTime[2]] +
                    ' ' +
                    this.dateTimeArray[3][this.dateTime[3]] +
                    ':' +
                    this.dateTimeArray[4][this.dateTime[4]];
                dateTimeStr =
                    this.dateTimeArray[0][this.dateTime[0]] +
                    '-' +
                    this.dateTimeArray[1][this.dateTime[1]] +
                    '-' +
                    this.dateTimeArray[2][this.dateTime[2]] +
                    'T' +
                    this.dateTimeArray[3][this.dateTime[3]] +
                    ':' +
                    this.dateTimeArray[4][this.dateTime[4]] +
                    ':00.000Z';
            } else {
                dateTime =
                    this.dateTimeArray[0][this.dateTime[0]] +
                    '-' +
                    this.dateTimeArray[1][this.dateTime[1]] +
                    '-' +
                    this.dateTimeArray[2][this.dateTime[2]] +
                    ' ' +
                    this.dateTimeArray[3][this.dateTime[3]] +
                    ':' +
                    this.dateTimeArray[4][this.dateTime[4]] +
                    ':' +
                    this.dateTimeArray[5][this.dateTime[5]];
                dateTimeStr =
                    this.dateTimeArray[0][this.dateTime[0]] +
                    '-' +
                    this.dateTimeArray[1][this.dateTime[1]] +
                    '-' +
                    this.dateTimeArray[2][this.dateTime[2]] +
                    'T' +
                    this.dateTimeArray[3][this.dateTime[3]] +
                    ':' +
                    this.dateTimeArray[4][this.dateTime[4]] +
                    ':' +
                    this.dateTimeArray[5][this.dateTime[5]] +
                    '.000Z';
            }
            this.$emit('datetimeChange', {
                detail: {
                    value: dateTime,
                    valueStr: dateTimeStr
                }
            });
        },

        changeDateTimeColumn(e) {
            var arr = this.dateTime;
            var dateArr = this.dateTimeArray;
            arr[e.detail.column] = e.detail.value;
            dateArr[2] = utils.getMonthDay(dateArr[0][arr[0]], dateArr[1][arr[1]]);
			this.dateTimeArray = dateArr
			this.dateTime = arr
        }
    },
    mounted() {
        // 处理小程序 attached 生命周期
        this.attached();
    },
    created: function () {}
};
</script>
<style>
.icon-box-img {
    position: absolute;
    left: 5px;
    top: 8px;
    height: 10px;
    color: #ddd;
}
.weui-input {
    width: 200px;
    height: 2.5em;
    min-height: 2.5em;
    line-height: 2.5em;
    position: relative;
    border-radius: 3px;
}
</style>

input-select.vue组件代码如下,如需要可自取:

<template>
    <view class="select-box">
        <view :class="isShow ? 'select-current-open' : 'select-current'" @tap.stop.prevent="openClose">
            <input @input="bindinput"  @blur="inputBlur" class="current-name" :placeholder="placeholder" v-model="inputValue" />
        </view>
        <view class="option-list" v-if="isShow" @tap.stop.prevent="optionTap" style="overflow-y: auto; overflow-x: hidden; max-height: 200px">
            <text :data-index="index" :class="'option ' + (item.selection ? 'selection' : '')" v-for="(item, index) in result" :key="item.id">{{ item[label] }}</text>
        </view>
    </view>
</template>

<script>
export default {
    data() {
        return {
            result: [],
            //转换后的候选项数据
            selection: 'selection',
            //选中样式
            inputValue: '',
            //输入框的值
            isShow: false,
            index: null,
            // 选中的下标
            inputFocus: false //输入框是否有焦点
        };
    },

    props: {
        options: {
            type: Array,
            default: () => []
        },
        label: {
            type: String,
            default: 'name'
        },
        value: {
            type: String,
            default: ''
        },
        placeholder: {
            type: String,
            default: '请选择'
        }
    },

    watch: {
        //监听数据变化
        inputValue: function (value) {},
		options: function (value) {
			this.result = value
			},
		value: {
		      handler(newValue, oldVal) {
		        this.inputValue = newValue
		      },
		      immediate: true
		    }
    },

    methods: {
        attached() {
            // 属性名称转换, 如果不是 { id: '', name:'' } 格式,则转为 { id: '', name:'' } 格式
            let result = [];
            if (this.key !== 'id' || this.text !== 'name' || this.text !== 'yes') {
                for (let item of this.options) {
                    let { [this.key]: id, [this.text]: name, [this.selection]: selection } = item;
                    result.push({
                        id,
                        name,
                        selection
                    });
                }
            }
			this.result = result
        },

        optionTap(e) {
            let that = this;
            let resuleObj = {
                flag: true
            }; //传递父组件的值.flag 表示是否是新增的 . true是新增,false不是新增
            this.index = e.target.dataset.index;
			this.inputValue = that.result[that.index][that.label]

            //选中的id
            var id = this.result[this.index].id;
            for (var i = 0; i < this.options.length; i++) {
                if (this.options[i].id == id) {
                    this.options[i].selection = true;
                    resuleObj.id = this.options[i].id;
                    resuleObj.flag = false;
                } else {
                    this.options[i].selection = false;
                }
            }
			this.isShow = false
			this.result = this.options
            resuleObj.value = that.inputValue;
            //调用父组件方法,并传参
            this.$emit('change', {
                detail: resuleObj
            });
        },

        openClose() {
            //如果是获取到焦点的状况下,就不关闭下拉选项
            if (this.inputFocusFun && this.isShow) {
                return;
            }
            var that = this;
			this.isShow = !that.isShow
            if (!this.isShow) {
                this.closeSetInputValue();
            }
            //只要操作当前项,就是获取到当前项的焦点
            this.$emit('focus', {
                detail: {
                    value: true
                }
            });
        },

        // 此方法供父组件调用
        close() {
			this.isShow = false
            this.closeSetInputValue();
        },

        closeSetInputValue() {
            //通过close和openClose方法隐藏选项时,设置inputValue的值
            let that = this;
            let inputValue = this.inputValue;
            //如果为空,直接返回
            if (!inputValue) {
                return;
            }
            //返回的数据结构
            let resuleObj = {
                flag: true
            };
            for (let i = 0; i < this.options.length; i++) {
                if (this.options[i][this.label] == inputValue) {
                    this.options[i].selection = true;
                    resuleObj.id = this.options[i].id;
                    resuleObj.flag = false;
                } else {
                    this.options[i].selection = false;
                }
            }
            resuleObj.value = that.inputValue;
            //调用父组件方法,并传参
            this.$emit('change', {
                detail: resuleObj
            });
        },

        inputFocusFun() {
			this.inputFocus = true
        },

        inputBlur() {
			this.inputFocus = false
        },

        bindinput(e) {
            var keyWord = e.detail.value;
            this.inputValue = e.detail.value;
            var tempresult = [];
            if (keyWord) {
                var obj = {
                    id: -1
                };
                obj[this.label] = keyWord;
                tempresult.push(obj);
            }
            for (var i = 0; i < this.options.length; i++) {
                if (this.options[i][this.label] == keyWord) {
                    this.options[i].selection = true;
                    tempresult.push(this.options[i]);
                    tempresult.splice(0, 1);
                    continue;
                }
                if (this.options[i][this.label].indexOf(keyWord) != -1) {
                    this.options[i].selection = false;
                    tempresult.push(this.options[i]);
                }
            }
			this.result = tempresult
        }
    },

    mounted() {
        // 处理小程序 attached 生命周期
        this.attached();
    },

    created: function () {}
};
</script>
<style>
.select-box {
    position: relative;
    width: 100%;
    font-size: 17px;
}

.select-current {
    position: relative;
    width: 100%;
    padding: 0 20px 0 6px;
    border: 1rpx solid #ddd;
    border-radius: 1px;
    box-sizing: border-box;
    line-height: 32px;
}

.select-current::after {
    position: absolute;
    display: block;
    right: 10px;
    top: 15px;
    content: '';
    width: 0;
    height: 0;
    border: 4px solid transparent;
    border-top: 5px solid #999;
}

.select-current-open {
    position: relative;
    width: 100%;
    padding: 0 20px 0 6px;
    border: 1rpx solid #ddd;
    border-radius: 1px;
    box-sizing: border-box;
    line-height: 32px;
}

.select-current-open::after {
    position: absolute;
    display: block;
    right: 10px;
    top: 10px;
    content: '';
    width: 0;
    height: 0;
    border: 4px solid transparent;
    border-bottom: 5px solid #999;
}

.selection {
    color: #00bbff;
}

.current-name {
    display: block;
    width: 85%;
    height: 32px;
    word-wrap: normal;
    overflow: hidden;
}

.option-list {
    position: absolute;
	font-size: 14px;
    left: 0;
    width: 100%;
    border-radius: 6rpx;
    box-sizing: border-box;
    z-index: 99;
    border: 1px solid #ddd;
    border-top: none;
    background-color: #fff;
}

.option {
    display: block;
    width: 100%;
    line-height: 32px;
    height: 32px;
    border-bottom: 1px solid #eee;
    padding: 0 6px;
}

.option:last-child {
    border-bottom: none;
    padding-bottom: 0;
}

</style>

总结:

在实际项目中,你可以根据需求进一步完善组件功能,如添加自定义子组件、自定义样式等。掌握组件封装技巧,将有助于提高你的uniapp开发效率。

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2176916.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《北方牧业》是什么级别的期刊?是正规期刊吗?能评职称吗?

问题解答 问&#xff1a;《中国动物检疫》是不是核心期刊&#xff1f; 答&#xff1a;不是&#xff0c;是知网收录的正规学术期刊。 问&#xff1a;《中国动物检疫》级别&#xff1f; 答&#xff1a;省级。主管单位&#xff1a;河北省畜牧局 主办单…

【win11】关闭windows11系统的讲述人

如何关闭windows11系统的讲述人&#xff0c;经常误触启动 讲述人&#xff08;Narrator&#xff09; 设置里找到讲述人&#xff08;Narrator&#xff09; 开关讲述人及快捷键

物联网行业中天线定制的激光直接成型 (LDS)技术

01 什么是lds技术&#xff1f; 普通的手机天线都被安装在手机的主板上&#xff1a; 在当今的智能手机中&#xff0c;我们常见的手机天线通常都被巧妙地安装在手机的主板上。这些天线承担着接收和发送信号的重要任务&#xff0c;是实现通信功能的关键组件之一。 而 LDS 天线…

运放信号失真原因分析——增益带宽积,压摆率

运放失真原因分析——增益带宽积&#xff0c;压摆率 运放失真原因分析 运放失真原因分析——增益带宽积&#xff0c;压摆率一、压摆率二、仿真模拟电路1.OPAx333的增益带宽积以及压摆率参数2.将输入信号的频率设置为10kHz3.将输入信号的频率设置为25kHz4.失真原因分析 总结 一、…

【MySQL实战45讲4-5】索引

文章目录 索引的定义索引的常见模型哈希表有序数组二叉搜索树 InnoDB的索引模型索引维护页分裂页合并页分裂和页合并的影响避免页分裂 覆盖索引最左前缀原则索引下推 索引的定义 索引的出现其实就是为了提高数据查询的效率&#xff0c;就像书的目录一样。一本500页的书&#x…

全开源/彩虹晴天多功能系统源码/知识付费系统/虚拟商城系统/完美可用+修复改良版

源码简介&#xff1a; 2024年最新全开源的彩虹晴天多功能系统源码&#xff0c;它可以作为知识付费系统、虚拟商城系统&#xff0c;完美可用&#xff0c;并且是修复改良版。 最让人兴奋的是&#xff0c;搭建这个系统完全不需要授权&#xff0c;不管是在国内还是国外的服务器&am…

Mybatis-Mapper接口方式

目录 配置方式和接口方式的区别 示例&#xff1a;Mapper接口方式实现数据库的CRUD 配置方式和接口方式的区别 Mybatis框架在配置方式的情况下&#xff0c;对数据库的CRUD操作直接通过SqlSession对象来操作&#xff0c;常用的方法有select、insert、update、delete等方法&…

企业内训|提示词工程师高阶技术内训-某运营商研发团队

近日&#xff0c;TsingtaoAI为某运营商技术团队交付提示词工程师高级技术培训&#xff0c;本课程为期2天&#xff0c;深入探讨深度学习与大模型技术在提示词生成与优化、客服大模型产品设计等业务场景中的应用。内容涵盖了深度学习前沿理论、大模型技术架构设计与优化、以及如何…

MySQL高阶1988-找出没所学校的最低分数要求

目录 题目 准备数据 分析数据 总结 题目 每年&#xff0c;学校会公布学生申请所需的最低分数要求。学校根据所有学生的考试成绩来决定其最低分数要求。 学校希望确保即使 每 一个满足分数要求的学生都申请该学校&#xff0c;学校也有足够的能力接纳每一个学生。学校也希望…

undeclared identifier ‘UNITY_PREV_MATRIX_M‘ - Unity Shader自己写URP,引用内部 hlsl

碰到这样的问题&#xff0c;居然非常淡定 这个链接里说了问题&#xff1a; 一个哥们A问&#xff0c;为什么include urp common.hlsl 提示莫名其妙 另一个哥们B说&#xff0c;这个issue 说了&#xff0c;可能是这个原因&#xff08;也没正面答&#xff09; 从issue我们知道&a…

手机软件何时统一——桥接模式

文章目录 手机软件何时统一——桥接模式凭什么你的游戏我不能玩紧耦合的程序演化合成&#xff0f;聚合复用原则松耦合的程序桥接模式桥接模式基本代码 手机软件何时统一——桥接模式 凭什么你的游戏我不能玩 时间&#xff1a;5月31日20点  地点&#xff1a;大鸟房间  人物…

从0学习React(5)---通过例子体会setState

上篇文章中&#xff0c;我们讲到了通过setState来更新组件的状态。我觉得上篇文章我讲的已经是比较详细的了&#xff0c;而且讲的很通俗易懂。但是上篇文章终归还是理论&#xff0c;没有实践&#xff0c;所以还是学了个表面而已。这篇文章我就结合一点实践来讲讲怎么使用这个se…

Java语言程序设计基础篇_编程练习题**18.37 (希尔伯特曲线)

目录 题目&#xff1a;**18.37 (希尔伯特曲线) 代码示例 代码逻辑解释 1. 初始化与布局 2. 绘制逻辑 3. 绘制过程 输出结果 题目&#xff1a;**18.37 (希尔伯特曲线) 希尔伯特曲线&#xff0c;由德国数学家希尔伯特于1891年第一个给出描述&#xff0c;是一种空间填充曲…

0108 Spring Boot启动过程

Spring Boot 的启动过程可以分为以下几个关键步骤&#xff1a; 1. SpringApplication 初始化 Spring Boot 应用的启动是通过调用 SpringApplication.run() 方法完成的。在这个过程中&#xff0c;Spring Boot 会通过 SpringApplication 类对应用进行初始化&#xff0c;包括设置…

ERP系统委外工单管理

1. 委外工单管理概念 1.1 定义与目的 委外工单管理是ERP系统中的一个关键组成部分&#xff0c;它涉及到将企业内部无法或不宜自行完成的生产任务&#xff0c;通过工单的形式委托给外部供应商进行加工、制造或服务的过程。委外工单管理的目的在于优化资源配置、降低生产成本、…

【Qt+Python项目构建】- 01-首次配置 Qt Creator 14.01 for Python

前言&#xff1a; 如何用QT实现Python的配置的交互界面。本文从0开始&#xff0c;进行实践的介绍。 在上一节里面&#xff0c;我们做了社区版本的配置&#xff1a; https://blog.csdn.net/yellow_hill/article/details/142597007?spm1001.2014.3001.5501 这一节&#xff0…

Android开发仿抖音底部加载进度条

Android开发仿抖音底部加载进度条 仿抖音底部加载进度条&#xff0c;从中间向两头伸的动画 一、思路&#xff1a; 自定义VideoLoadingBar控件 二、效果图&#xff1a; 三、关键代码&#xff1a; // 联系&#xff1a;893151960 public class VideoLoadingBar extends View …

阿布量化:基于 Python 的量化交易框架

阿布量化&#xff08;AbuQuant&#xff09; 是一个开源的量化交易框架&#xff0c;专为金融领域的研究者和交易者设计。它基于 Python 语言开发&#xff0c;提供了一整套从数据获取、策略开发、回测分析到交易执行的解决方案。阿布量化不仅能够帮助用户快速实现量化策略的设计与…

IDEA:Picked up _JAVA_OPTIONS: -Xmx512M

_JAVA_OPTIONS 是一个环境变量&#xff0c;它可以用来设置 Java 虚拟机的启动参数 如果要取消它在idea中显示&#xff0c;如图所示将其删除即可

C# 游戏引擎中的协程

前言 书接上回&#xff0c;我谈到了Unity中的协程的重要性&#xff0c;虽然协程不是游戏开发“必要的”&#xff0c;但是它可以在很多地方发挥优势。 为了在Godot找回熟悉的Unity协程开发手感&#xff0c;不得不自己做一个协程系统&#xff0c;幸运的是&#xff0c;有了Unity的…