-曾老湿, 江湖人称曾老大。
-笔者QQ:133411023、253097001
-笔者交流群:198571640
-笔者微信:z133411023


-多年互联网运维工作经验,曾负责过大规模集群架构自动化运维管理工作。
-擅长Web集群架构与自动化运维,曾负责国内某大型金融公司运维工作。
-devops项目经理兼DBA。
-开发过一套自动化运维平台(功能如下):

1)整合了各个公有云API,自主创建云主机。
2)ELK自动化收集日志功能。
3)Saltstack自动化运维统一配置管理工具。
4)Git、Jenkins自动化代码上线及自动化测试平台。
5)堡垒机,连接Linux、Windows平台及日志审计。
6)SQL执行及审批流程。
7)慢查询日志分析web界面。


收入和支出的切换功能-JS实现


Types组件的JS
<template>
    <ul class="types">
        <li :class="type === '-' && 'selected' " @click="selectType('-')">支出</li>
        <li :class="type === '+' && 'selected' " @click="selectType('+')">收入</li>
    </ul>
</template>

<script>
    export default {
        name: "Types",
        data() {
            return {
                type: '-' // '-'表示支出,'+'表示收入
            }
        },
        methods: {
            selectType(type) { // type 只能是'-'和'+'中的一个
                if (type !== '-' && type !== '+') {
                    throw new Error('type is unknown')
                }
                this.type = type
            }
        }
    };
</script>

<style lang="scss" scoped>
    .types {
        background: #c4c4c4;
        display: flex;
        text-align: center;
        font-size: 24px;

        > li {
            width: 50%;
            height: 64px;
            display: flex;
            justify-content: center;
            align-items: center;
            position: relative;
            /*当前li被选中使用 &,如果直接写.selected代表li里面的selected*/
            &.selected::after {
                content: '';
                position: absolute;
                bottom: 0;
                left: 0;
                width: 100%;
                height: 4px;
                background: #333;
            }
        }
    }
</style>

收入和支出的切换功能-TS实现

<template>
    <ul class="types">
        <li :class="type === '-' && 'selected' " @click="selectType('-')">支出</li>
        <li :class="type === '+' && 'selected' " @click="selectType('+')">收入</li>
    </ul>
</template>

<script lang="ts">
    import Vue from "vue";
    import {Component} from "vue-property-decorator";

    @Component //装饰器
    export default class Types extends Vue {
        type = "-"; // '-'表示支出,'+'表示收入
        selectType(type: string) { // type 只能是'-'和'+'中的一个
            if (type !== "-" && type !== "+") {
                throw new Error("type is unknown");
            }
            this.type = type;
        }
    }
  </script>

<style lang="scss" scoped>
    .types {
        background: #c4c4c4;
        display: flex;
        text-align: center;
        font-size: 24px;

        > li {
            width: 50%;
            height: 64px;
            display: flex;
            justify-content: center;
            align-items: center;
            position: relative;
            /*当前li被选中使用 &,如果直接写.selected代表li里面的selected*/
            &.selected::after {
                content: '';
                position: absolute;
                bottom: 0;
                left: 0;
                width: 100%;
                height: 4px;
                background: #333;
            }
        }
    }
</style>

TS添加props

Types.vue 设置props

<template>
    <ul class="types">
        <li :class="type === '-' && 'selected' " @click="selectType('-')">支出</li>
        <li :class="type === '+' && 'selected' " @click="selectType('+')">收入</li>
    </ul>
</template>

<script lang="ts">
    import Vue from "vue";
    import {Component, Prop} from "vue-property-decorator";
    // Prop告诉Vue xxx  不是data 是prop
     // Number 告诉 Vue xxx 运行时是个Number
     // xxx 属性名
     // number | undefined 告诉 TS xxx的编译时类型

    @Component //装饰器
    export default class Types extends Vue {
        type = "-"; // '-'表示支出,'+'表示收入
        @Prop(Number) xxx: number | undefined;

        selectType(type: string) { // type 只能是'-'和'+'中的一个
            if (type !== "-" && type !== "+") {
                throw new Error("type is unknown");
            }
            this.type = type;
        }

        mounted() {
            console.log(this.xxx);
        }
    }
</script>

<style lang="scss" scoped>
    .types {
        background: #c4c4c4;
        display: flex;
        text-align: center;
        font-size: 24px;

        > li {
            width: 50%;
            height: 64px;
            display: flex;
            justify-content: center;
            align-items: center;
            position: relative;
            /*当前li被选中使用 &,如果直接写.selected代表li里面的selected*/
            &.selected::after {
                content: '';
                position: absolute;
                bottom: 0;
                left: 0;
                width: 100%;
                height: 4px;
                background: #333;
            }
        }
    }
</style>

Money.vue 引用 props

<template>
    <div id="app">
        <Layout class-prefix="layout">
            <NumberPad/>
            <Types :xxx="333"/>  <!-- 引用 types中的props传递过来的xxx -->
            <Notes/>
            <Tags/>
        </Layout>
    </div>
</template>

<script lang="ts">
    import NumberPad from "@/components/Money/NumberPad.vue";
    import Types from "@/components/Money/Types.vue";
    import Notes from "@/components/Money/Notes.vue";
    import Tags from "@/components/Money/Tags.vue";

    export default {
        name: "Money",
        components: {Tags, Notes, Types, NumberPad},
    };
</script>

<style lang="scss">
    .layout-content {
        /*border: 3px solid red;*/
        display: flex;
        flex-direction: column-reverse;
    }
</style>

注意:undefined是默认值,不得不加,但是一旦加上,比如要在任何时候都检查他是不是undefined

        mounted() {
            if(this.xxx === undefined){
                console.log('undefined');
            }else {
                console.log(this.xxx);
            }
        }

WebStrom设置Vue的TS模板

如此依赖,我们写TS就需要设置一下Vue的模板

<template>
    <div>#[[$END$]]#</div>
</template>

<script lang="ts">
import Vue from 'vue';
import {Component} from 'vue-property-decorator';
@Component
export default class ${COMPONENT_NAME} extends Vue {

}
</script>

计算器组件功能

<template>
    <div class="numberPad">
        <div class="output">{{output}}</div>
        <div class="buttions">
            <button @click="inputContent">1</button>
            <button @click="inputContent">2</button>
            <button @click="inputContent">3</button>
            <button @click="remove">删除</button>
            <button @click="inputContent">4</button>
            <button @click="inputContent">5</button>
            <button @click="inputContent">6</button>
            <button @click="clear">清空</button>
            <button @click="inputContent">7</button>
            <button @click="inputContent">8</button>
            <button @click="inputContent">9</button>
            <button @click="ok" class="ok">OK</button>
            <button @click="inputContent" class="zero">0</button>
            <button @click="inputContent">.</button>
        </div>
    </div>
</template>

<script lang="ts">
    import Vue from "vue";
    import {Component, Prop} from "vue-property-decorator";

    @Component //装饰器
    export default class NumberPad extends Vue {
        output = "0";

        inputContent(event: MouseEvent) {
            const button = (event.target as HTMLButtonElement); // 强制指定类型,如果不指定,你没法判断event是什么类型
            console.log(button.textContent);
            const input = button.textContent!;  // 感叹号和下面一行用法一样,就是这个东西不会为空
            // const input = button.textContent as string;
            if (this.output.length === 16) {
                return;
            }
            if (this.output === "0") {
                if ("0123456789".indexOf(input) >= 0) {
                    this.output = input;
                } else {
                    this.output += input;
                }
                return;
            }
            if (this.output.indexOf(".") >= 0 && input === ".") {
                return;
            }
            this.output += input;
        }

        remove() {
            if(this.output.length === 1){
                this.output = '0';
            }else {
                this.output = this.output.slice(0, -1);
            }
        }

        clear() {
            this.output = '0'
        }

        // ok() {
        // }
    }
</script>

<style lang="scss" scoped>
    @import "~@/assets/style/helper.scss";

    .numberPad {
        .output {
            font-size: 36px;
            font-family: Consolas, monospace; // 等宽字体,Consolas是windows的,monospace是编程字体
            padding: 9px 16px;
            text-align: right;
            height: 72px;
            @extend %innerShadow;
        }

        .buttions {
            // 代替clearfix,但是如果写在这里就会很重复,那么我们就使用变量的方式,我与重复不共戴天。
            /*&::after{*/
            /*    content: '';*/
            /*    display: block;*/
            /*    clear: both;*/
            /*}*/
            // 此处引用helper.scss的变量
            @extend %clearFix;

            > button {
                width: 25%;
                height: 64px;
                float: left;
                background: transparent;
                border: none;


                &.ok {
                    height: 64*2px;
                    float: right;
                }

                &.zero {
                    width: 25*2%;
                }

                $bg: lightgreen;

                &:nth-child(1) {
                    background: $bg;
                }

                &:nth-child(2), &:nth-child(5) {
                    background: darken($bg, 4%);
                }

                &:nth-child(3), &:nth-child(6), &:nth-child(9) {
                    background: darken($bg, 4*2%);
                }

                &:nth-child(4), &:nth-child(7), &:nth-child(10) {
                    background: darken($bg, 4*3%);
                }

                &:nth-child(8), &:nth-child(11), &:nth-child(13) {
                    background: darken($bg, 4*4%);
                }

                &:nth-child(14) {
                    background: darken($bg, 4*5%);
                }

                &:nth-child(12) {
                    background: darken($bg, 4*6%);
                }
            }
        }
    }
</style>

备注组件功能

<template>
    <label class="notes">
        {{value}}
        <span class="name">备注</span>
        <input type="text" :value="value"
               @input="onInput"
               placeholder="在这里添加备注">
    </label>
</template>

<script lang="ts">
    import Vue from "vue";
    import {Component} from "vue-property-decorator";

    @Component
    export default class Notes extends Vue {
        value = "";

        onInput(event: KeyboardEvent) {
            const input = event.target as HTMLInputElement;
            this.value = input.value;
        }
    }
</script>

<style lang="scss" scoped>
    .notes {
        font-size: 14px;
        background: #f5f5f5;
        padding-left: 16px;
        display: flex;
        align-items: center;

        .name {
            padding-right: 16px;
        }

        input {
            height: 64px;
            flex-grow: 1;
            background: transparent;
            border: none;
            padding-right: 16px;
        }
    }
</style>

知识点:如果代码如下

:value = "x" 
@input = "x = $event.target.value"

可以使用v-model

v-model="x"

修改代码使用v-model
<template>
    <div>
        <label class="notes">
            <span class="name">备注</span>
            <input type="text"
                   v-model="value"
                   placeholder="在这里添加备注">
        </label>
    </div>
</template>

<script lang="ts">
    import Vue from "vue";
    import {Component} from "vue-property-decorator";

    @Component
    export default class Notes extends Vue {
        value = "";
    }
</script>

<style lang="scss" scoped>
    .notes {
        font-size: 14px;
        background: #f5f5f5;
        padding-left: 16px;
        display: flex;
        align-items: center;

        .name {
            padding-right: 16px;
        }

        input {
            height: 64px;
            flex-grow: 1;
            background: transparent;
            border: none;
            padding-right: 16px;
        }
    }
</style>

标签组件功能


tags.vue
<template>
    <div class="tags">
        <div class="new">
            <button @click="create">新增标签</button>
        </div>
        <ul class="current">
            <li v-for="tag in dataSource" :key="tag"
                :class="{selected: selectedTags.indexOf(tag)>=0}"
                @click="toggle(tag)">{{tag}}
            </li>
        </ul>
    </div>
</template>

<script lang="ts">
    import Vue from "vue";
    import {Component, Prop} from "vue-property-decorator";

    @Component
    export default class Tags extends Vue {
        @Prop(Array) readonly dataSource: string[] | undefined;
        selectedTags: string[] = [];

        toggle(tag: string) {
            const index = this.selectedTags.indexOf(tag);
            if (index >= 0) {
                this.selectedTags.splice(index, 1);
            } else {
                this.selectedTags.push(tag);
            }
        }

        create() {
            const name = window.prompt("请输入标签名:");
            if (name === "") {
                alert("标签名不能为空");
            } else if (this.dataSource) {
                this.$emit("update:dataSource", [...this.dataSource, name]);
            }
            //if (this.dataSource) {
            //      this.dataSource.push(name!); // 不能改外部数据
            //}
        }
    }
</script>

<style lang="scss" scoped>
    .tags {
        font-size: 14px;
        padding: 16px;
        flex-grow: 1;
        display: flex;
        flex-direction: column-reverse;

        > .current {
            display: flex;
            flex-wrap: wrap;

            > li {
                $bg: #d9d9d9;
                background: $bg;
                /* 只有一行内容的时候可以让line-height=height让字体居中 */
                $h: 24px;
                height: $h;
                line-height: $h;
                border-radius: $h/2;
                padding: 0 16px;
                margin-right: 12px;
                margin-top: 4px;

                &.selected {
                    background: darken($bg, 50%);
                    color: #fff;
                }
            }
        }

        > .new {
            padding-top: 16px;

            button {
                background: transparent;
                border: none;
                color: #999;
                border-bottom: 1px solid;
                padding: 0 4px;
            }
        }
    }
</style>

Money.vue
<template>
    <div id="app">
        <Layout class-prefix="layout">
            <NumberPad/>
            <Types/>  <!-- 引用 types中的props传递过来的xxx -->
            <Notes/>
            <Tags :data-source.sync="tags"/>
        </Layout>
    </div>
</template>

<script lang="js">
    import NumberPad from "@/components/Money/NumberPad.vue";
    import Types from "@/components/Money/Types.vue";
    import Notes from "@/components/Money/Notes.vue";
    import Tags from "@/components/Money/Tags.vue";

    export default {
        name: "Money",
        components: {Tags, Notes, Types, NumberPad},
        data() {
            return {
                tags: ['衣', '食', '住', '行', '奢侈品']
            }
        }
    };
</script>

<style lang="scss">
    .layout-content {
        display: flex;
        flex-direction: column-reverse;
    }
</style>