flexbox/src/App.vue

625 lines
19 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup lang="ts">
import {ref, watch, onMounted, reactive} from "vue";
/** 子项默认宽度 */
const itemWidth = ref(10);
/** 父Flex属性Flex容器 默认值 */
const flexContainerForm: { [key: string]: any } = reactive({
"flex-direction": "row",
"flex-wrap": "nowrap",
"justify-content": "flex-start",
"align-items": "stretch",
"align-content": "stretch",
});
/** 选项值,默认值 */
const flexContainerList = {
"flex-direction": {
title: "flex-direction",
titleTooltip: "排列方向",
list: ["row", "row-reverse", "column", "column-reverse"],
tooltip: ["主轴为水平方向,起点在左端", "row反方向", "主轴为垂直方向,起点在上沿", "column反方向"],
modelValue: "row"
},
"flex-wrap": {
title: "flex-wrap",
titleTooltip: "排不下,换行",
list: ["nowrap", "wrap", "wrap-reverse"],
tooltip: ["不换行", "换行,第一行在上方", "换行,第一行在下方"],
modelValue: "nowrap",
},
"justify-content": {
title: "justify-content",
titleTooltip: "主轴上的对齐方式",
list: ["flex-start", "flex-end", "center", "space-between", "space-around"],
tooltip: ["左对齐", "右对齐", "居中", "两端对齐,项目之间的间隔都相等", "每个项目两侧的间隔相等"],
modelValue: "flex-start",
},
"align-items": {
title: "align-items",
titleTooltip: "交叉轴上如何对齐",
list: ["stretch", "flex-start", "flex-end", "center", "baseline"],
tooltip: ["项目未设置高度或设为auto将占满整个容器的高度", "交叉轴的起点对齐", "交叉轴的终点对齐", "交叉轴的中点对齐", "目的第一行文字的基线对齐"],
modelValue: "stretch",
},
"align-content": {
title: "align-content",
titleTooltip: "多根轴线的对齐方式",
list: [
"stretch",
"flex-start",
"flex-end",
"center",
"space-between",
"space-around",
],
tooltip: ["轴线占满整个交叉轴", "与交叉轴的起点对齐", "与交叉轴的终点对齐", "与交叉轴的中点对齐", "与交叉轴两端对齐,轴线之间的间隔平均分布", "每根轴线两侧的间隔都相等"],
modelValue: "stretch",
},
};
/** 子项初始个数 */
const itemCount = 5;
/** 当前子项总数 */
let itemListCount = ref(itemCount);
/** 子项-单项默认值 */
const itemFormDefaultValue: { [key: string]: number | string } = {
order: 0,
"flex-grow": 0,
"flex-shrink": 1,
"flex-basis": "auto",
"align-self": "auto",
};
/** align-self 选项 */
const itemAlignSelf = [
"auto",
"stretch",
"center",
"flex-start",
"flex-end",
"baseline",
];
const itemAlignSelfTooltip = [
"为父元素的align-items值没有父容器则为stretch",
"适合容器",
"容器的中央",
"容器的开头",
"容器的末端",
"容器的基线",
];
/** 子项列表 JSON 数据 */
let flexItemForm: {
[key: string]: any;
} = reactive({});
/** 增加子项 */
const addItem = () => {
itemListCount.value++;
flexItemForm[`item-` + itemListCount.value] = JSON.parse(
JSON.stringify(itemFormDefaultValue)
);
};
/** 减少子项 */
const removeItem = (key: string | number) => {
delete flexItemForm[key];
delete flexItemFormStyle[key];
};
/** 横杆后的字母变大写 */
const toUp = (str: string) => {
let newStr = "";
let arr = str.split("-"); //先用字符串分割成数组
arr.forEach((item, index) => {
if (index > 0) {
return (newStr += item.replace(item[0], item[0].toUpperCase()));
} else {
return (newStr += item);
}
});
return newStr;
};
/** flexBox style */
let flexContainerFormStyle: { [key: string]: any } = reactive({});
const flexContainerFormStyleFun = () => {
Object.keys(flexContainerForm).forEach((key) => {
flexContainerFormStyle["display"] = "flex";
flexContainerFormStyle["height"] = "100%";
flexContainerFormStyle["minHeight"] = "160px";
flexContainerFormStyle["padding"] = "10px";
flexContainerFormStyle["backgroundColor"] = "goldenrod";
flexContainerFormStyle[toUp(key)] = flexContainerForm[key];
});
};
/** flexContainerForm 父属性 默认值监听*/
watch(flexContainerForm, () => {
// flexContainerFormStyle = {};
flexContainerFormStyleFun();
});
/** 子项 style 生成 */
let flexItemFormStyle: { [key: string]: any } = reactive({});
const flexItemFormStyleFun = () => {
Object.keys(flexItemForm).forEach((ikey) => {
let valueObj: any = {};
let flexItemFormIkey = flexItemForm[ikey];
Object.keys(flexItemFormIkey).forEach((_ikeys) => {
// valueObj['backgroundColor'] = 'cornsilk';
valueObj["width"] = itemWidth.value + "%";
// valueObj['minHeight'] = '80px';
// valueObj['display'] = 'inline-block';
valueObj[toUp(_ikeys)] = flexItemFormIkey[_ikeys];
});
flexItemFormStyle[ikey] = valueObj;
});
};
/** 监听子项 和 宽度样式变化 */
watch([flexItemForm, itemWidth], () => {
// flexItemFormStyle = {};
flexItemFormStyleFun();
});
/** 初始 子项 默认值 */
const initialFlexItemForm = () => {
for (let index = 0; index < itemCount; index++) {
flexItemForm[`item-` + index] = JSON.parse(
JSON.stringify(itemFormDefaultValue)
);
}
}
/**
* 准备html和css 打印准备
*/
/** 盒子dom */
let fiexBoxDom: any = ref(null);
/** 要输出的html */
let fiexBoxDomHtml: any = ref(``);
/** 外层 CSS */
let fiexBoxCssString: any = ref(``);
/** 子项 CSS */
let fiexItemCssString: any = ref(``);
/** 生成HTML方法 */
const fiexSubmitHtml = () => {
let html = `<div class="flexbox">`;
// 获取所有子项
Object.keys(flexItemForm).forEach((ikey, index) => {
const itemClass = "item-" + (index + 1);
html += `<div class="flex-item ${itemClass}">${itemClass}</div>`;
});
html += `</div>`;
fiexBoxDomHtml.value = html;
};
/** 生成CSS方法{} */
const fiexSubmitCss = () => {
/** 增加CSS外层{} */
const addKuohao = (css: string) => {
return `{` + '\n' + css + '\n' + `}`;
};
const flexboxitemDom = fiexBoxDom.value.querySelectorAll(".flexboxitem");
let css: string = ".flex-item{display: inline-block; min-height: 80px; background-color: cornsilk; margin: 5px; overflow: hidden;}";
flexboxitemDom.forEach((dom: any, index: string) => {
css += `.item-` + (index + 1) + addKuohao(dom.style.cssText);
});
fiexBoxCssString.value = `.flexbox{` + fiexBoxDom.value.style.cssText + `}`;
fiexItemCssString.value = css;
};
/** 获取打印html和css */
const fiexSubmit = () => {
fiexSubmitHtml();
fiexSubmitCss();
};
watch([flexContainerFormStyle, flexItemFormStyle], () => {
fiexSubmitHtml();
fiexSubmitCss();
})
/** 初始页面加载后只执行一次 */
onMounted(() => {
initialFlexItemForm();
flexContainerFormStyleFun();
flexItemFormStyleFun();
});
/**
* 底部
*/
const showTime: any = ref('');
const showTimeYear: any = ref(0);
const checkTime = function (i: any) {
if (i < 10) {
i = "0" + i;
}
return i;
};
const showTimeFun = function () {
const nowdate = new Date();
let year = nowdate.getFullYear(),
month = nowdate.getMonth() + 1,
date = nowdate.getDate(),
day = nowdate.getDay(),
week = ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"],
h: string | number = nowdate.getHours(),
m: string | number = nowdate.getMinutes(),
s: string | number = nowdate.getSeconds();
h = checkTime(h);
m = checkTime(m);
s = checkTime(s);
// 年
showTimeYear.value = year.toString();
// 日期时间
showTime.value = year + "年" + month + "月" + date + "日" + week[day] + " " + h + ":" + m + ":" + s;
};
showTimeFun();
setInterval(() => {
showTimeFun();
}, 1000); //反复执行函数
</script>
<template>
<a-layout class="layout">
<a-layout-header>
<a-typography-title bold>CSS3 Flexbox 在线演示</a-typography-title>
</a-layout-header>
<a-layout-content>
<a-row class="layout-content-box" justify="justify-content" :wrap="false" :gutter="15"
:style="{ alignItems: 'stretch',flexGrow:1}">
<a-col :md="12" :span="24">
<div class="mainContent">
<a-typography-title :heading="4" bold>子项宽度</a-typography-title>
<a-space fill size="medium" class="itemWidth">
<a-typography-text>宽度</a-typography-text>
<a-input-number
:style="{ width: '80px' }"
:min="10"
:max="100"
v-model="itemWidth">
<template #suffix>%</template>
</a-input-number>
<a-slider
v-model="itemWidth"
:min="10"
:max="100"
:step="1"
show-ticks
/>
</a-space>
<a-typography-title :heading="4" bold>父Flex属性Flex容器</a-typography-title>
<a-row justify="justify-content" :wrap="false">
<a-col
class="containerCol"
v-for="(item, key) in flexContainerList"
:key="key"
>
<a-typography-title :heading="6" bold>
<a-tooltip
:content="item.titleTooltip"
background-color="goldenrod"
mini>
<strong>{{ item.title }}</strong>
</a-tooltip>
</a-typography-title>
<a-radio-group
direction="vertical"
v-model="flexContainerForm[key]"
>
<a-tooltip
v-for="(value, keys) in item.list"
:key="keys"
:content="item.tooltip[keys]"
background-color="#00bd7e"
mini
>
<a-radio :value="value">
{{ value }}
</a-radio>
</a-tooltip>
</a-radio-group>
</a-col>
</a-row>
<a-button
@click="fiexSubmit"
:style="{margin:'1em 0'}"
type="primary"
status="success" long>
生成
</a-button>
<div class="mainContentBox">
<a-split :style="{
height: '100%',
width: '100%',
backgroundColor:'var(--color-neutral-2)',
maxHeight:'358px'
}">
<template #first>
<a-split direction="vertical" :style="{height: '99.9%'}">
<template #first>
<a-typography-paragraph copyable>
<highlightjs language="html" :autodetect="false" :code="fiexBoxDomHtml"></highlightjs>
</a-typography-paragraph>
</template>
<template #second>
<a-typography-paragraph copyable>
<highlightjs language="css" :autodetect="false" :code="fiexBoxCssString"></highlightjs>
</a-typography-paragraph>
</template>
</a-split>
</template>
<template #second>
<a-typography-paragraph copyable>
<highlightjs language="css" :autodetect="false" :code="fiexItemCssString"></highlightjs>
</a-typography-paragraph>
</template>
</a-split>
</div>
</div>
</a-col>
<a-col :md="12" :span="24">
<div class="mainContent">
<a-typography-title :heading="4" bold>
实现效果
<a-button
type="primary"
@click="addItem"
:style="{ float: 'right' }"
>
<template #icon>
<icon-plus/>
</template>
新增子项
</a-button>
</a-typography-title>
<div class="mainContentBox">
<div
class="flexbox"
:style="flexContainerFormStyle"
ref="fiexBoxDom"
>
<div
class="flexboxitem"
v-for="(_, indexKey, index) in flexItemForm"
:key="index"
:style="flexItemFormStyle[indexKey]"
>
<a-card :title="(index + 1).toString()"
:bordered="false"
:header-style="{backgroundColor:'azure',fontWeight:'bold'}">
<template #extra>
<a-button
type="text"
shape="circle"
@click="removeItem(indexKey)"
>
<icon-close/>
</a-button>
</template>
<a-list>
<a-list-item>
<a-tooltip content="order排列顺序数值越小排列越靠前" mini>
<a-input-number
v-model="flexItemForm[indexKey].order"
:min="0"
model-event="input"
/>
</a-tooltip>
</a-list-item>
<a-list-item>
<a-tooltip content="flex-grow项目的放大比例" mini>
<a-input-number
v-model="flexItemForm[indexKey]['flex-grow']"
:min="0"
model-event="input"
/>
</a-tooltip>
</a-list-item>
<a-list-item>
<a-tooltip content="flex-shrink项目的缩小比例" mini>
<a-input-number
v-model="flexItemForm[indexKey]['flex-shrink']"
:min="1"
:max="100"
model-event="input"
/>
</a-tooltip>
</a-list-item>
<a-list-item>
<a-tooltip
content="flex-basis计算主轴是否有多余空间设为跟width或height属性一样的值将占据固定空间"
mini>
<a-input
v-model="flexItemForm[indexKey]['flex-basis']"
allow-clear
/>
</a-tooltip>
</a-list-item>
<a-list-item>
<a-tooltip content="align-self单个项目有与其他项目不一样的对齐方式覆盖align-items属性"
mini>
<a-select
v-model="flexItemForm[indexKey]['align-self']"
>
<a-tooltip
v-for="(ivalue,ikeys) in itemAlignSelf"
:key="ikeys"
:content="itemAlignSelfTooltip[ikeys]"
mini
background-color="#00bd7e">
<a-option>
{{ ivalue }}
</a-option>
</a-tooltip>
</a-select>
</a-tooltip>
</a-list-item>
</a-list>
</a-card>
</div>
</div>
</div>
</div>
</a-col>
</a-row>
</a-layout-content>
<a-layout-footer ref="showTimeDom">
<a-divider :margin="10" :size="2">
<icon-heart/>
</a-divider>
<a-row class="footer" :gutter="24">
<a-col :span="12">
<a-typography-text type="success">{{ showTime }}</a-typography-text>
</a-col>
<a-col :span="12">
<a-typography-text type="success">
Copyright © 2015-{{ showTimeYear }}
<a-link target="_blank" href="https://beian.miit.gov.cn/" status="success" rel="noopener">鲁ICP备10001546号
</a-link>
</a-typography-text>
</a-col>
</a-row>
</a-layout-footer>
</a-layout>
</template>
<style lang="less" scoped>
.arco-layout {
height: 100vh;
.arco-layout-header,
.arco-layout-footer,
.arco-layout-content {
display: flex;
/*垂直排列*/
flex-direction: column;
justify-content: center;
font-stretch: condensed;
text-align: center;
padding: 15px;
}
.arco-layout-header,
.arco-layout-footer {
height: 64px;
background-color: #00bd7e;
}
//头部
:deep(.arco-layout-header) {
.arco-typography {
margin: 0;
}
}
//底部
.arco-layout-footer {
background-color: transparent;
.arco-link {
font-size: initial;
}
}
}
//内容
.arco-layout :deep(.arco-layout-content) {
text-align: left;
height: calc(100vh - 64px - 64px);
padding-top: 0;
.layout-content-box {
.itemWidth .arco-space-item:last-child {
flex-grow: 1;
.arco-slider-ticks .arco-slider-tick {
margin-top: 5px;
}
}
.containerCol {
flex: 1 0 20%;
width: 20%;
flex-basis: 0;
}
.mainContent {
display: flex;
flex-direction: column;
height: 100%;
.mainContentBox {
flex-grow: 1;
pre {
white-space: normal;
code {
background: #23241f;
color: #f8f8f2;
//padding: 10px;
border: none;
}
}
.flexbox {
min-height: 300px;
.flexboxitem {
//min-height: 80px;
background-color: cornsilk;
display: inline-block;
margin: 5px;
overflow: hidden;
.arco-card {
background: transparent;
.arco-card-body {
padding: 5px;
.arco-list-wrapper,
.arco-list-medium .arco-list-content-wrapper .arco-list-content > .arco-list-item {
padding: 0;
}
.arco-list-bordered {
border: none;
.arco-input-wrapper,
.arco-select-view-single {
background-color: transparent;
}
}
}
}
}
}
}
}
}
}
@media (max-width: 768px) {
.arco-layout :deep(.arco-layout-content) {
height: auto;
.layout-content-box {
flex-wrap: nowrap;
}
}
}
</style>