Vue3商品SKU多规格编辑组件

2024-09-07 14:12

本文主要是介绍Vue3商品SKU多规格编辑组件,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

商品SKU多规格组件主要用于电商平台的商品详情页,帮助买家在选择商品时能够根据不同的规格(如颜色、尺码、容量等)进行筛选和购买。这种组件的设计旨在提升用户体验,简化购买流程,并确保买家能够准确地选择到自己需要的商品规格。以下是对商品SKU多规格组件的详细解析:

1. 定义与功能

  • SKU(Stock Keeping Unit)定义:SKU即库存量单位,是指宝贝的销售属性集合,供买家在下单时点选,如“规格”、“颜色分类”、“尺码”等。部分SKU的属性值可以由卖家自定义编辑,部分则不可编辑。
  • 功能:商品SKU多规格组件允许买家在商品详情页直接查看并选择商品的多种规格,同时展示每种规格对应的库存数量、价格等信息。这有助于买家快速了解商品的各种选项,并做出购买决策。

2. 组件特点

  • 数据驱动:现代的商品SKU多规格组件通常采用数据驱动的方式,即组件从服务器获取商品规格数据,并根据这些数据动态渲染出可供买家选择的规格选项。这种方式提高了组件的灵活性和可扩展性。
  • 用户友好:组件的设计注重用户体验,通过清晰的界面布局和交互设计,使买家能够轻松理解和选择商品的规格。
  • 实时更新:组件能够实时从服务器获取商品的库存信息和价格信息,确保买家在选择规格时能够看到最新的数据。

3. 实现方式

  • 前端开发:商品SKU多规格组件通常使用前端技术进行开发。这里使用了Vue3现代前端框架中,可以通过组件化的方式实现商品SKU多规格组件的复用和灵活配置。
  • 后端支持:组件需要后端服务的支持,以获取商品的规格数据、库存信息、价格信息等。后端服务可以通过API接口向前端组件提供这些数据。
  • 数据交互:前端组件与后端服务之间通过HTTP请求和响应进行数据交互。前端组件发送请求获取数据,后端服务处理请求并返回数据给前端组件进行展示。

4. 应用场景

  • 电商平台:商品SKU多规格组件广泛应用于各类电商平台,如淘宝、京东、天猫等。这些平台上的商品种类繁多,规格多样,使用SKU多规格组件可以大大提升买家的购物体验。
  • 零售店铺:对于拥有线上销售渠道的零售店铺来说,商品SKU多规格组件也是一个不可或缺的工具。它可以帮助店铺更好地展示商品信息,提高销售额。

5. 组件实现

<template><div class="diygw-col-24"><div v-if="specs.length > 0"><VueDraggable v-bind="{ animation: 200, disabled: false, ghostClass: 'ghost' }" v-model="specs" class="list-group" ghost-class="ghost"><div v-for="(element, itemIndex) in specs" class="box sku-spec flex flex-direction-column margin-bottom"><div class="flex margin-bottom-xs align-center justify-between"><el-input style="width: 200px !important; flex: unset" placeholder="请输入规格名称" v-model="element.title"> </el-input><div class="toolbar"><i class="diyicon handle diyicon-yidongxuanze" /><el-tooltip content="上移" placement="bottom"><i class="diy-icon-top" @click.stop="move(itemIndex, 'up')" /></el-tooltip><el-tooltip content="下移" placement="bottom"><i class="diy-icon-down" @click.stop="move(itemIndex, 'down')" /></el-tooltip><el-tooltip content="新增规格组" placement="bottom"><i class="diy-icon-add" @click.stop="addSpec()" /></el-tooltip><el-tooltip content="删除规格组" placement="bottom"><i class="diy-icon-close" @click.stop="removeSpec(itemIndex)" /></el-tooltip></div></div><div class="flex flex-wrap align-center"><VueDraggablev-bind="{ animation: 200, disabled: false, ghostClass: 'ghost' }"v-model="element.datas"class="flex flex-wrap align-center sku-spec-item"ghost-class="ghost"><div v-for="(subitem, subindex) in element.datas" class="margin-right-xs spec-item margin-bottom-xs" style="width: 150px"><el-input v-model="subitem.title"> </el-input><div class="spec-toolbar"><el-tooltip content="删除规格值" placement="bottom"><i class="diy-icon-close" @click.stop="removeSpecItem(itemIndex, subindex)" /></el-tooltip></div></div></VueDraggable><div class="margin-right-xs margin-bottom-xs text-xs" @click="addSpecIndex(itemIndex)">新增规格值</div></div></div></VueDraggable></div><el-button class="diygw-col-24 margin-bottom-xs" @click="addSpec">新增规格</el-button><DiySkutable v-model="skus" :specs="specs" :columns="columns" ref="skuref"></DiySkutable></div>
</template><script setup name="DiySku">
import { ElMessage } from 'element-plus';
import { useVModels } from '@vueuse/core';
import { VueDraggable } from 'vue-draggable-plus';
import DiySkutable from './skutable.vue';
const props = defineProps({skus: {type: Array,default: () => {return [];},},specs: {//规格配置type: Array,default: () => {return [];},},columns: {//自定义sku属性type: Array,default: () => {return [];},},filterable: {//是否开启sku搜索type: Boolean,default: true,},
});const emit = defineEmits(['update:specs', 'update:skus']);
const { skus, specs } = useVModels(props, emit);
const removeSpec = (index) => {specs.value.splice(index, 1);
};const removeSpecItem = (index, subindex) => {specs.value[index].datas.splice(subindex, 1);
};const addSpecIndex = (index) => {let id = specs.value[index].datas.length + specs.value[index].id;specs.value[index].datas.push({title: '',id: id + '' + new Date().getTime(),});
};const addSpec = () => {let id = (specs.value.length + 1) * 1000;specs.value.push({id: id,title: '',datas: [{title: '',id: id + '' + new Date().getTime(),},],});
};const move = (itemIndex, dir) => {let comps = specs.value;let item = comps[itemIndex];if (dir == 'up') {if (itemIndex == 0) {ElMessage.error('已经是第一个了!');return;}const swap = comps[itemIndex - 1];const tmp = swap;comps[itemIndex - 1] = item;comps[itemIndex] = tmp;} else {if (itemIndex == comps.length - 1) {ElMessage.error('已经是最一个了!');return;} else {const swap = comps[itemIndex + 1];const tmp = swap;comps[itemIndex + 1] = item;comps[itemIndex] = tmp;}}
};
</script>
<style lang="scss">
.sku-spec {padding: 6px;border-radius: 5px;border: 1px solid #ebeef5;.sku-spec-item {padding-left: 30px;padding-right: 5px;.spec-item {position: relative;.spec-toolbar {top: -2px;right: -3px;position: absolute;display: none;}&:hover {.spec-toolbar {display: block;}}}}
}
</style>
<template><div class="sku-table" v-if="specs.length > 0"><table class="diy-table diy-skutable"><tr><th v-for="(item, colIndex) in specs" :key="colIndex">{{ item.title }}</th><th v-for="(item, colIndex) in columns" :style="{ width: item.type == 'number' ? '100px' : 'auto' }" :key="colIndex + specs.length + 1">{{ item.title }}</th></tr><tr><td v-for="(item, colIndex) in specs" :key="colIndex"><el-select v-if="filterable" @change="resetTable" v-model="headerFilterParams[item.title]" placeholder="过滤查询" clearable><el-option label="全部" value=""></el-option><el-option v-for="(child, index2) in item.datas" :key="index2" :label="child.title" :value="child.title"></el-option></el-select><span v-else class="spec-title">{{ item.title }}</span></td><td v-for="(item, colIndex) in columns" :key="colIndex + specs.length + 1"><span v-if="item.readOnly">{{ item.title }}</span><diy-uploadinputclass="diygw-col-24"v-else-if="item.type == 'img'"@blur="() => {onBatchEdit(item.id);}"placeholder="批量修改"v-model="columnsValue[item.id]"/><el-input-number:min="0":precision="item.precision ? item.precision : 0":step="item.precision ? 0.1 : 1"v-else-if="item.type == 'number'"v-model="columnsValue[item.id]"controls-position="right":controls="false"class="sku-input-number"@blur="() => {onBatchEdit(item.id);}"@keyup.native.enter="() => {onBatchEdit(item.id);}"placeholder="批量修改"/><el-inputv-elseclass="diygw-col-24"v-model="columnsValue[item.id]"@blur="() => {onBatchEdit(item.id);}"@keyup.native.enter="() => {onBatchEdit(item.id);}"placeholder="批量修改"/></td></tr><tr v-for="(row, rowIndex) in renderTableRows" :key="row.id"><tdv-for="(child, colIndex) in row.columns":class="[child.shouldSetRowSpan ? '' : 'hide',rowIndex === rowLastCanSpan[colIndex] ? 'col-last-rowspan' : '',colIndex < specs.length - 1 ? 'row-span-style' : '',]":rowspan="child.shouldSetRowSpan ? assignRule[colIndex] : ''":key="colIndex"><span>{{ child.showValue }}</span></td><td v-for="(child, colIndex) in columns" :key="colIndex + columns.length + 1"><span v-if="child.readOnly">{{ row[child.id] }}</span><diy-uploadinputv-else-if="child.type == 'img'"@change="(value) => {checkValue(value, child, row);}":placeholder="child.title"v-model="row[child.id]"></diy-uploadinput><el-input-number:min="0":precision="child.precision ? child.precision : 0":step="child.precision ? 0.1 : 1"v-else-if="child.type == 'number'"v-model="row[child.id]"controls-position="right":controls="false"class="sku-input-number"@change="(value) => {checkValue(value, child, row);}":placeholder="child.title"/><el-inputv-elsev-model="row[child.id]"@change="(value) => {checkValue(value, child, row);}":placeholder="child.title"/></td></tr></table></div>
</template><script setup name="DiySkutable">
import { uuid } from '@/utils';
import { ElMessageBox } from 'element-plus';
import { onMounted, ref, watch, computed } from 'vue';
import DiyUploadinput from '@/components/upload/uploadinput.vue';
import { useVModel } from '@vueuse/core';const props = defineProps({modelValue: {type: Array,default: () => {return [];},},specs: {//规格配置type: Array,default: () => {return [];},},columns: {//自定义sku属性type: Array,default: () => {return [];},},filterable: {//是否开启sku搜索type: Boolean,default: true,},
});const emit = defineEmits(['update:modelValue']);
const modeldata = useVModel(props, 'modelValue', emit);const headerFilterParams = ref({});
const columnsValue = ref({});
const originTableRows = ref([]);
const renderTableRows = ref([]);onMounted(() => {render();
});function render() {originTableRows.value = createTable();renderTableRows.value = originTableRows.value;getData();
}function _resetRowSpan(table) {table.forEach((row, rowIndex) => {row.columns.forEach((column, columnIndex) => {column.shouldSetRowSpan = shouldSetRowSpan(rowIndex, columnIndex);});});
}
function createTable() {let tableData = [];let details = props.modelValue;let theaderFilterParams = {};props.specs.forEach((item) => {theaderFilterParams[item.title] = '';});headerFilterParams.value = theaderFilterParams;for (let i = 0; i < skuTotal.value; i++) {let columns = props.specs.map((t, j) => {let { title, id } = getShowValue(i, j);return {shouldSetRowSpan: shouldSetRowSpan(i, j),showValue: title,valueId: id,columnName: t.title,columnId: t.id,precision: t.precision,};});//获取当前组合let pattern = columns.map((t) => t.valueId).sort().toString();//从详情中找回同一个组合的sku数据let rowDetails = details.find((t) => t.specIds === pattern);//当数据长度不为0,并新增了大的规格if (details.length > 0 && details.length >= i && !rowDetails && details[0].specIds.split(',').length != columns.length) {rowDetails = details[i];}tableData.push({id: uuid(),...createSkuPropertyFields(columns, i, rowDetails),columns,});}return tableData;
}
function createSkuPropertyFields(columns, rowIndex, row) {return props.columns.reduce((res, item) => {if (row && row[item.id]) {res[item.id] = row[item.id] || '';} else {if (item.defaultValue) {// 设置默认值,可以为string或function,fuction时会传入行的索引和列的信息if (typeof item.defaultValue === 'string') {res[item.id] = item.defaultValue;} else if (typeof item.defaultValue === 'function') {res[item.id] = item.defaultValue({ columns, rowIndex });}} else if (columnsValue.value[item.id]) {res[item.id] = columnsValue.value[item.id];} else if (res.type == 'number') {res[item.id] = 0;} else {res[item.id] = '';}}return res;}, {});
}
function shouldSetRowSpan(rowIndex, colIndex) {return rowIndex % assignRule.value[colIndex] === 0;
}
function getShowValue(rowIndex, colIndex) {let datas = props.specs[colIndex].datas;let index;if (colIndex === props.specs.length - 1) {index = rowIndex % datas.length;} else {let step = assignRule.value[colIndex];index = Math.floor(rowIndex / step);if (index >= datas.length) {index = index % datas.length;}}return datas[index];
}function onBatchEdit(id) {ElMessageBox.confirm(`确认批量修改吗?`, '提示', {customClass: 'diygw-messagebox',confirmButtonText: '确认',cancelButtonText: '取消',type: 'warning',}).then(() => {let value = columnsValue.value[id];renderTableRows.value.forEach((item) => {item[id] = value;});getData();}).catch(() => {});
}
function checkValue(value, columnInfo, row) {// let { id, pattern } = columnInfo;// if (pattern) {// 	(row || columnsValue.value)[id] = value.replace(pattern, '');// }getData();
}
function getData() {let data = originTableRows.value.map((t) => {let columnObj = props.columns.reduce((res, item) => {res[item.id] = item.format ? item.format(t[item.id]) : t[item.id];return res;}, {});return {specIds: t.columns.map((t) => t.valueId).join(','),...columnObj,};});modeldata.value = data;
}const hasFilter = computed(() => {return Object.values(headerFilterParams.value).filter((t) => !!t).length > 0;
});const renderSpecs = computed(() => {return props.specs.map((t) => t.datas.length).map((t, index) => {return hasFilter.value ? (headerFilterParams.value[props.specs[index].title] ? 1 : t) : t;});
});const skuTotal = computed(() => {return renderSpecs.value.reduce((result, item) => result * item, renderSpecs.value.length ? 1 : 0);
});const assignRule = computed(() => {return renderSpecs.value.reduce((result, item, index) => {let preValue = result[index - 1];if (preValue) {result.push(preValue / item);} else {result.push(skuTotal.value / item);}return result;}, []);
});const rowLastCanSpan = computed(() => {let indexArr = Array.from(new Array(skuTotal.value).keys()); //生成行的索引数组//每列可以合并的最后一行的行索引数组,为了设置样式return assignRule.value.map((t, index, array) => {return index === array.length - 1 ? null : indexArr.filter((row) => row % t === 0).pop();});
});watch(() => props.specs,() => {render();},{deep: true,immediate: true,}
);
function resetTable() {if (hasFilter.value) {let trenderTableRows = originTableRows.value.filter((t) => {return t.columns.reduce((res, item) => {let filterValue = headerFilterParams.value[item.columnName];return filterValue ? res && item.showValue === filterValue : res;}, true);});_resetRowSpan(trenderTableRows);renderTableRows.value = trenderTableRows;} else {_resetRowSpan(originTableRows.value);renderTableRows.value = originTableRows.value;}
}
// watch(
// 	headerFilterParams,
// 	() => {
// 		resetTable();
// 	},
// 	{
// 		deep: true,
// 		immediate: true,
// 	}
// );
</script>

组件调用

<template><div class="container"><div class="el-card is-always-shadow diygw-col-24"><div class="el-card__body"><div class="mb8"><el-button type="primary" plain @click="onOpenAddModule"> <SvgIcon name="ele-Plus" />新增 </el-button><el-button type="danger" plain :disabled="state.multiple" @click="onTabelRowDel"> <SvgIcon name="ele-Delete" />删除 </el-button></div><el-table@selection-change="handleSelectionChange"v-loading="state.loading":data="state.tableData"stripebordercurrent-row-key="id"empty-text="没有数据"style="width: 100%"><el-table-column type="selection" width="55" align="center"></el-table-column><el-table-column label="姓名" prop="name" align="center"> </el-table-column><el-table-column label="性别" prop="sex" align="center"> </el-table-column><el-table-column label="邮箱" prop="email" align="center"> </el-table-column><el-table-column label="操作" align="center" fixed="right" width="180"><template #default="scope"><el-button type="primary" plain size="small" @click="onOpenEditModule(scope.row)"> <SvgIcon name="ele-Edit" />修改 </el-button><el-button v-if="scope.row.id != 0" type="danger" plain size="small" @click="onTabelRowDel(scope.row)"><SvgIcon name="ele-Delete" />删除</el-button></template></el-table-column></el-table><!-- 分页设置--><div v-show="state.total > 0"><el-divider></el-divider><el-paginationbackground:total="state.total":current-page="state.queryParams.pageNum":page-size="state.queryParams.pageSize"layout="total, sizes, prev, pager, next, jumper"@size-change="handleSizeChange"@current-change="handleCurrentChange"/></div></div><!-- 添加或修改参数配置对话框 --><el-dialog :fullscreen="isFullscreen" width="800px" v-model="isShowDialog" destroy-on-close title="CRUD表格" draggable center><template #header="{ close, titleId, titleClass }"><h4 :id="titleId" :class="titleClass">CRUD表格</h4><el-tooltip v-if="!isFullscreen" content="最大化" placement="bottom"><el-button class="diygw-max-icon el-dialog__headerbtn"><el-icon @click="isFullscreen = !isFullscreen"><FullScreen /></el-icon></el-button></el-tooltip><el-tooltip v-if="isFullscreen" content="返回" placement="bottom"><el-button class="diygw-max-icon el-dialog__headerbtn"><el-icon @click="isFullscreen = !isFullscreen"><Remove /></el-icon></el-button></el-tooltip></template><el-form class="flex flex-direction-row flex-wrap" :model="state.editForm" :rules="state.editFormRules" ref="editFormRef" label-width="80px"><div class="flex diygw-col-24"><el-form-item prop="sku" label="多规格"><diy-sku :columns="editFormData.skuColumns" v-model:skus="editForm.sku.skus" v-model:specs="editForm.sku.specs"></diy-sku></el-form-item></div></el-form><template #footer><div class="dialog-footer flex justify-center"><el-button @click="closeDialog"> 取 消 </el-button><el-button type="primary" @click="onSubmit" :loading="state.saveloading"> 保 存 </el-button></div></template></el-dialog></div><div class="clearfix"></div></div>
</template><script setup name="index">
import { ElMessageBox, ElMessage } from 'element-plus';
import { ref, toRefs, reactive, onMounted, getCurrentInstance, onUnmounted, unref } from 'vue';
import { deepClone, changeRowToForm } from '@/utils/other';
import { addData, updateData, delData, listData } from '@/api';import { useRouter, useRoute } from 'vue-router';
import { storeToRefs } from 'pinia';
import { useUserInfo } from '@/stores/userInfo';import DiySku from '@/components/sku/index.vue';const stores = useUserInfo();
const { userInfos } = storeToRefs(stores);
const { proxy } = getCurrentInstance();
const router = useRouter();
const route = useRoute();
const globalOption = ref(route.query);
const getParamData = (e, row) => {row = row || {};let dataset = e.currentTarget ? e.currentTarget.dataset : e;if (row) {dataset = Object.assign(dataset, row);}return dataset;
};const navigateTo = (e, row) => {let dataset = getParamData(e, row);let { type } = dataset;if ((type == 'page' || type == 'inner' || type == 'href') && dataset.url) {router.push({path: (dataset.url.startsWith('/') ? '' : '/') + dataset.url,query: dataset,});} else {ElMessage.error('待实现点击事件');}
};const state = reactive({userInfo: userInfos.value,// 遮罩层loading: true,// 选中数组ids: [],// 非单个禁用single: true,// 非多个禁用multiple: true,// 弹出层标题title: '',// 总条数total: 0,tableData: [],// 是否显示弹出层isFullscreen: false,isShowDialog: false,saveloading: false,editFormData: {skuColumns: [{ title: '图片地址', id: 'thumb', type: 'img' },{ title: '价格', id: 'price', type: 'number' },{ title: '划线价格', id: 'linePrice', type: 'number' },{ title: '数量', id: 'amount', type: 'number' },{ title: '备注', id: 'sku', type: 'text' },],},queryParams: {pageNum: 1,pageSize: 10,},queryParamsRules: {},editForm: {id: undefined,sku: {skus: [],specs: [],},},editFormRules: {},
});
const {userInfo,editFormData,queryParams,multiple,tableData,loading,title,single,total,isShowDialog,editForm,ids,saveloading,isFullscreen,
} = toRefs(state);
const editFormRef = ref(null);const editFormInit = deepClone(state.editForm);// 打开弹窗
const openDialog = (row) => {if (row.id && row.id != undefined && row.id != 0) {state.editForm = changeRowToForm(row, state.editForm);} else {initForm();}state.isShowDialog = true;state.saveloading = false;
};// 关闭弹窗
const closeDialog = (row) => {proxy.mittBus.emit('onEditAdmintableModule', row);state.isShowDialog = false;
};// 保存
const onSubmit = () => {const formWrap = unref(editFormRef);if (!formWrap) return;formWrap.validate((valid) => {if (valid) {state.saveloading = true;if (state.editForm.id != undefined && state.editForm.id != 0) {updateData('/sys/user', state.editForm).then(() => {ElMessage.success('修改成功');state.saveloading = false;closeDialog(state.editForm); // 关闭弹窗}).finally(() => {state.saveloading = false;});} else {addData('/sys/user', state.editForm).then(() => {ElMessage.success('新增成功');state.saveloading = false;closeDialog(state.editForm); // 关闭弹窗}).finally(() => {state.saveloading = false;});}} else {ElMessage.error('验证未通过!');}});
};
// 表单初始化,方法:`resetFields()` 无法使用
const initForm = () => {state.editForm = deepClone(editFormInit);
};
const queryParamsInit = deepClone(state.queryParams);
/** 查询CRUD表格列表 */
const handleQuery = () => {state.loading = true;listData('/sys/user', state.queryParams).then((response) => {state.tableData = response.rows;state.total = response.total;state.loading = false;});
};
/** 重置按钮操作 */
const resetQuery = () => {state.queryParams = deepClone(queryParamsInit);handleQuery();
};// 打开新增CRUD表格弹窗
const onOpenAddModule = (row) => {row = [];state.title = '添加CRUD表格';initForm();openDialog(row);
};
// 打开编辑CRUD表格弹窗
const onOpenEditModule = (row) => {state.title = '修改CRUD表格';openDialog(row);
};
/** 删除按钮操作 */
const onTabelRowDel = (row) => {const id = row.id || state.ids;ElMessageBox({message: '是否确认删除选中的CRUD表格?',title: '警告',showCancelButton: true,confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning',}).then(function () {return delData('/sys/user', id).then(() => {handleQuery();ElMessage.success('删除成功');});});
};
// 多选框选中数据
const handleSelectionChange = (selection) => {state.ids = selection.map((item) => item.id);state.single = selection.length != 1;state.multiple = !selection.length;
};//分页页面大小发生变化
const handleSizeChange = (val) => {state.queryParams.pageSize = val;handleQuery();
};
//当前页码发生变化
const handleCurrentChange = (val) => {state.queryParams.pageNum = val;handleQuery();
};
const init = async () => {};
// 页面加载时
onMounted(async () => {await init();handleQuery();proxy.mittBus.on('onEditAdmintableModule', () => {handleQuery();});
});// 页面卸载时
onUnmounted(() => {proxy.mittBus.off('onEditAdmintableModule');
});
</script><style lang="scss" scoped>
.container {font-size: 12px;
}
</style>

已集成进开源项目组件:diygw-ui-admin: 基于vite、vue3.x 、router、pinia、Typescript、Element plus等,适配手机、平板、pc 的后台开源免费模板库

这篇关于Vue3商品SKU多规格编辑组件的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1145279

相关文章

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义

JS常用组件收集

收集了一些平时遇到的前端比较优秀的组件,方便以后开发的时候查找!!! 函数工具: Lodash 页面固定: stickUp、jQuery.Pin 轮播: unslider、swiper 开关: switch 复选框: icheck 气泡: grumble 隐藏元素: Headroom

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

【 html+css 绚丽Loading 】000046 三才归元阵

前言:哈喽,大家好,今天给大家分享html+css 绚丽Loading!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏+关注哦 💕 目录 📚一、效果📚二、信息💡1.简介:💡2.外观描述:💡3.使用方式:💡4.战斗方式:💡5.提升:💡6.传说: 📚三、源代码,上代码,可以直接复制使用🎥效果🗂️目录✍️

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

如何在页面调用utility bar并传递参数至lwc组件

1.在app的utility item中添加lwc组件: 2.调用utility bar api的方式有两种: 方法一,通过lwc调用: import {LightningElement,api ,wire } from 'lwc';import { publish, MessageContext } from 'lightning/messageService';import Ca

计算机毕业设计 大学志愿填报系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点赞 👍 收藏 ⭐评论 📝 🍅 文末获取源码联系 👇🏻 精彩专栏推荐订阅 👇🏻 不然下次找不到哟~Java毕业设计项目~热门选题推荐《1000套》 目录 1.技术选型 2.开发工具 3.功能

Vue3项目开发——新闻发布管理系统(六)

文章目录 八、首页设计开发1、页面设计2、登录访问拦截实现3、用户基本信息显示①封装用户基本信息获取接口②用户基本信息存储③用户基本信息调用④用户基本信息动态渲染 4、退出功能实现①注册点击事件②添加退出功能③数据清理 5、代码下载 八、首页设计开发 登录成功后,系统就进入了首页。接下来,也就进行首页的开发了。 1、页面设计 系统页面主要分为三部分,左侧为系统的菜单栏,右侧

【VUE】跨域问题的概念,以及解决方法。

目录 1.跨域概念 2.解决方法 2.1 配置网络请求代理 2.2 使用@CrossOrigin 注解 2.3 通过配置文件实现跨域 2.4 添加 CorsWebFilter 来解决跨域问题 1.跨域概念 跨域问题是由于浏览器实施了同源策略,该策略要求请求的域名、协议和端口必须与提供资源的服务相同。如果不相同,则需要服务器显式地允许这种跨域请求。一般在springbo

HTML提交表单给python

python 代码 from flask import Flask, request, render_template, redirect, url_forapp = Flask(__name__)@app.route('/')def form():# 渲染表单页面return render_template('./index.html')@app.route('/submit_form',